Simon Hunt
Committed by Gerrit Code Review

ONOS-1479 - GUI Topology Overlay Work - (WIP)

- UiExtension now uses Builder Pattern; added topology overlay factory.
- Refactored UiExtensionTest (and other classes) to use builder.
- Created UiTopoOverlayFactory, UiTopoOverlay, and TopoOverlayCache.
- Started implementation of TrafficOverlay.
- Inject TopoOverlayCache into TopologyViewMessageHandler; added TopoSelectOverlay request handler.
- Modified UiExtensionManager to create traffic overlay.
- Augmented UiWebSocket to create overlays on demand, and inject overlay cache into topo view message handler.
- added client side wiring to switch overlays.

Change-Id: I6f99596aefb3b87382517ce929d268a2447545ee
...@@ -62,7 +62,9 @@ public class IntentPerfUi { ...@@ -62,7 +62,9 @@ public class IntentPerfUi {
62 ); 62 );
63 63
64 private UiExtension uiExtension = 64 private UiExtension uiExtension =
65 - new UiExtension(views, this::newHandlers, getClass().getClassLoader()); 65 + new UiExtension.Builder(getClass().getClassLoader(), views)
66 + .messageHandlerFactory(this::newHandlers)
67 + .build();
66 68
67 private IntentPerfCollector collector; 69 private IntentPerfCollector collector;
68 70
......
...@@ -15,61 +15,47 @@ ...@@ -15,61 +15,47 @@
15 */ 15 */
16 package org.onosproject.ui; 16 package org.onosproject.ui;
17 17
18 -import com.google.common.collect.ImmutableList;
19 import org.slf4j.Logger; 18 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory; 19 import org.slf4j.LoggerFactory;
21 20
22 import java.io.InputStream; 21 import java.io.InputStream;
22 +import java.util.ArrayList;
23 import java.util.List; 23 import java.util.List;
24 24
25 +import static com.google.common.base.Preconditions.checkArgument;
25 import static com.google.common.base.Preconditions.checkNotNull; 26 import static com.google.common.base.Preconditions.checkNotNull;
26 27
27 /** 28 /**
28 * User interface extension. 29 * User interface extension.
29 */ 30 */
30 -public class UiExtension { 31 +public final class UiExtension {
31 32
32 private final Logger log = LoggerFactory.getLogger(getClass()); 33 private final Logger log = LoggerFactory.getLogger(getClass());
33 34
34 private static final String VIEW_PREFIX = "app/view/"; 35 private static final String VIEW_PREFIX = "app/view/";
36 + private static final String EMPTY = "";
37 + private static final String SLASH = "/";
38 + private static final String CSS_HTML = "css.html";
39 + private static final String JS_HTML = "js.html";
35 40
36 - private final String prefix;
37 private final ClassLoader classLoader; 41 private final ClassLoader classLoader;
42 + private final String resourcePath;
38 private final List<UiView> views; 43 private final List<UiView> views;
39 private final UiMessageHandlerFactory messageHandlerFactory; 44 private final UiMessageHandlerFactory messageHandlerFactory;
40 - 45 + private final UiTopoOverlayFactory topoOverlayFactory;
41 - /** 46 +
42 - * Creates a user interface extension for loading CSS and JS injections 47 +
43 - * from {@code css.html} and {@code js.html} resources, respectively. 48 + // private constructor - only the builder calls this
44 - * 49 + private UiExtension(ClassLoader cl, String path, List<UiView> views,
45 - * @param views list of contributed views 50 + UiMessageHandlerFactory mhFactory,
46 - * @param messageHandlerFactory optional message handler factory 51 + UiTopoOverlayFactory toFactory) {
47 - * @param classLoader class-loader for user interface resources 52 + this.classLoader = cl;
48 - */ 53 + this.resourcePath = path;
49 - public UiExtension(List<UiView> views, 54 + this.views = views;
50 - UiMessageHandlerFactory messageHandlerFactory, 55 + this.messageHandlerFactory = mhFactory;
51 - ClassLoader classLoader) { 56 + this.topoOverlayFactory = toFactory;
52 - this(views, messageHandlerFactory, null, classLoader);
53 } 57 }
54 58
55 - /**
56 - * Creates a user interface extension using custom resource prefix. It
57 - * loads CSS and JS injections from {@code path/css.html} and
58 - * {@code prefix/js.html} resources, respectively.
59 - *
60 - * @param views list of user interface views
61 - * @param messageHandlerFactory optional message handler factory
62 - * @param path resource path prefix
63 - * @param classLoader class-loader for user interface resources
64 - */
65 - public UiExtension(List<UiView> views,
66 - UiMessageHandlerFactory messageHandlerFactory,
67 - String path, ClassLoader classLoader) {
68 - this.views = checkNotNull(ImmutableList.copyOf(views), "Views cannot be null");
69 - this.messageHandlerFactory = messageHandlerFactory;
70 - this.prefix = path != null ? (path + "/") : "";
71 - this.classLoader = checkNotNull(classLoader, "Class loader must be specified");
72 - }
73 59
74 /** 60 /**
75 * Returns input stream containing CSS inclusion statements. 61 * Returns input stream containing CSS inclusion statements.
...@@ -77,7 +63,7 @@ public class UiExtension { ...@@ -77,7 +63,7 @@ public class UiExtension {
77 * @return CSS inclusion statements 63 * @return CSS inclusion statements
78 */ 64 */
79 public InputStream css() { 65 public InputStream css() {
80 - return getStream(prefix + "css.html"); 66 + return getStream(resourcePath + CSS_HTML);
81 } 67 }
82 68
83 /** 69 /**
...@@ -86,7 +72,7 @@ public class UiExtension { ...@@ -86,7 +72,7 @@ public class UiExtension {
86 * @return JavaScript inclusion statements 72 * @return JavaScript inclusion statements
87 */ 73 */
88 public InputStream js() { 74 public InputStream js() {
89 - return getStream(prefix + "js.html"); 75 + return getStream(resourcePath + JS_HTML);
90 } 76 }
91 77
92 /** 78 /**
...@@ -106,18 +92,28 @@ public class UiExtension { ...@@ -106,18 +92,28 @@ public class UiExtension {
106 * @return resource input stream 92 * @return resource input stream
107 */ 93 */
108 public InputStream resource(String viewId, String path) { 94 public InputStream resource(String viewId, String path) {
109 - return getStream(VIEW_PREFIX + viewId + "/" + path); 95 + return getStream(VIEW_PREFIX + viewId + SLASH + path);
110 } 96 }
111 97
112 /** 98 /**
113 - * Returns message handler factory. 99 + * Returns message handler factory, if one was defined.
114 * 100 *
115 - * @return message handlers 101 + * @return message handler factory
116 */ 102 */
117 public UiMessageHandlerFactory messageHandlerFactory() { 103 public UiMessageHandlerFactory messageHandlerFactory() {
118 return messageHandlerFactory; 104 return messageHandlerFactory;
119 } 105 }
120 106
107 + /**
108 + * Returns the topology overlay factory, if one was defined.
109 + *
110 + * @return topology overlay factory
111 + */
112 + public UiTopoOverlayFactory topoOverlayFactory() {
113 + return topoOverlayFactory;
114 + }
115 +
116 +
121 // Returns the resource input stream from the specified class-loader. 117 // Returns the resource input stream from the specified class-loader.
122 private InputStream getStream(String path) { 118 private InputStream getStream(String path) {
123 InputStream stream = classLoader.getResourceAsStream(path); 119 InputStream stream = classLoader.getResourceAsStream(path);
...@@ -128,4 +124,76 @@ public class UiExtension { ...@@ -128,4 +124,76 @@ public class UiExtension {
128 } 124 }
129 125
130 126
127 + /**
128 + * UI Extension Builder.
129 + */
130 + public static class Builder {
131 + private ClassLoader classLoader;
132 +
133 + private String resourcePath = EMPTY;
134 + private List<UiView> views = new ArrayList<>();
135 + private UiMessageHandlerFactory messageHandlerFactory = null;
136 + private UiTopoOverlayFactory topoOverlayFactory = null;
137 +
138 + /**
139 + * Create a builder with the given class loader.
140 + * Resource path defaults to "".
141 + * Views defaults to an empty list.
142 + * Both Message and TopoOverlay factories default to null.
143 + *
144 + * @param cl the classloader
145 + */
146 + public Builder(ClassLoader cl, List<UiView> views) {
147 + checkNotNull(cl, "Must provide a class loader");
148 + checkArgument(views.size() > 0, "Must provide at least one view");
149 + this.classLoader = cl;
150 + this.views = views;
151 + }
152 +
153 + /**
154 + * Set the resource path. That is, path to where the CSS and JS
155 + * files are located. This value should
156 + *
157 + * @param path resource path
158 + * @return self, for chaining
159 + */
160 + public Builder resourcePath(String path) {
161 + this.resourcePath = path == null ? EMPTY : path + SLASH;
162 + return this;
163 + }
164 +
165 + /**
166 + * Sets the message handler factory for this extension.
167 + *
168 + * @param mhFactory message handler factory
169 + * @return self, for chaining
170 + */
171 + public Builder messageHandlerFactory(UiMessageHandlerFactory mhFactory) {
172 + this.messageHandlerFactory = mhFactory;
173 + return this;
174 + }
175 +
176 + /**
177 + * Sets the topology overlay factory for this extension.
178 + *
179 + * @param toFactory topology overlay factory
180 + * @return self, for chaining
181 + */
182 + public Builder topoOverlayFactory(UiTopoOverlayFactory toFactory) {
183 + this.topoOverlayFactory = toFactory;
184 + return this;
185 + }
186 +
187 + /**
188 + * Builds the UI extension.
189 + *
190 + * @return UI extension instance
191 + */
192 + public UiExtension build() {
193 + return new UiExtension(classLoader, resourcePath, views,
194 + messageHandlerFactory, topoOverlayFactory);
195 + }
196 +
197 + }
198 +
131 } 199 }
......
...@@ -107,8 +107,9 @@ public abstract class UiMessageHandler { ...@@ -107,8 +107,9 @@ public abstract class UiMessageHandler {
107 void exec(String eventType, long sid, ObjectNode payload) { 107 void exec(String eventType, long sid, ObjectNode payload) {
108 RequestHandler requestHandler = handlerMap.get(eventType); 108 RequestHandler requestHandler = handlerMap.get(eventType);
109 if (requestHandler != null) { 109 if (requestHandler != null) {
110 - log.debug("process {} event...", eventType);
111 requestHandler.process(sid, payload); 110 requestHandler.process(sid, payload);
111 + } else {
112 + log.warn("no request handler for event type {}", eventType);
112 } 113 }
113 } 114 }
114 115
......
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;
19 +
20 +import org.slf4j.Logger;
21 +import org.slf4j.LoggerFactory;
22 +
23 +/**
24 + * Represents user interface topology view overlay.
25 + */
26 +public class UiTopoOverlay {
27 +
28 + private final Logger log = LoggerFactory.getLogger(getClass());
29 +
30 + private final String id;
31 +
32 + /**
33 + * Creates a new user interface topology view overlay descriptor.
34 + *
35 + * @param id overlay identifier
36 + */
37 + public UiTopoOverlay(String id) {
38 + this.id = id;
39 + }
40 +
41 + /**
42 + * Returns the identifier for this overlay.
43 + *
44 + * @return the identifier
45 + */
46 + public String id() {
47 + return id;
48 + }
49 +
50 + /**
51 + * Callback invoked to initialize this overlay, soon after creation.
52 + * This default implementation does nothing.
53 + */
54 + public void init() {
55 + }
56 +
57 + /**
58 + * Callback invoked when this overlay is activated.
59 + */
60 + public void activate() {
61 + log.debug("Overlay '{}' Activated", id);
62 + }
63 +
64 + /**
65 + * Callback invoked when this overlay is deactivated.
66 + */
67 + public void deactivate() {
68 + log.debug("Overlay '{}' Deactivated", id);
69 + }
70 +
71 + /**
72 + * Callback invoked to destroy this instance by cleaning up any
73 + * internal state ready for garbage collection.
74 + * This default implementation does nothing.
75 + */
76 + public void destroy() {
77 + }
78 +}
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;
19 +
20 +import java.util.Collection;
21 +
22 +/**
23 + * Abstraction of an entity capable of producing one or more topology
24 + * overlay handlers specific to a given user interface connection.
25 + */
26 +public interface UiTopoOverlayFactory {
27 +
28 + /**
29 + * Produces a collection of new overlay handlers.
30 + *
31 + * @return collection of new overlay handlers
32 + */
33 + Collection<UiTopoOverlay> newOverlays();
34 +}
...@@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; ...@@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList;
19 import org.junit.Test; 19 import org.junit.Test;
20 20
21 import java.io.IOException; 21 import java.io.IOException;
22 +import java.util.List;
22 23
23 import static com.google.common.io.ByteStreams.toByteArray; 24 import static com.google.common.io.ByteStreams.toByteArray;
24 import static org.junit.Assert.*; 25 import static org.junit.Assert.*;
...@@ -29,29 +30,111 @@ import static org.onosproject.ui.UiView.Category.OTHER; ...@@ -29,29 +30,111 @@ import static org.onosproject.ui.UiView.Category.OTHER;
29 */ 30 */
30 public class UiExtensionTest { 31 public class UiExtensionTest {
31 32
33 + private static final String FOO_ID = "foo";
34 + private static final String FOO_LABEL = "Foo View";
35 + private static final String BAR_ID = "bar";
36 + private static final String BAR_LABEL = "Bar View";
37 + private static final String CUSTOM = "custom";
38 +
39 +
40 + private static final UiView FOO_VIEW = new UiView(OTHER, FOO_ID, FOO_LABEL);
41 + private static final UiView BAR_VIEW = new UiView(OTHER, BAR_ID, BAR_LABEL);
42 + private static final UiView HIDDEN_VIEW = new UiViewHidden(FOO_ID);
43 +
44 + private static final UiMessageHandlerFactory MH_FACTORY = () -> null;
45 + private static final UiTopoOverlayFactory TO_FACTORY = () -> null;
46 +
47 + private final ClassLoader cl = getClass().getClassLoader();
48 +
49 + private List<UiView> viewList;
50 + private UiExtension ext;
51 + private String css;
52 + private String js;
53 + private UiView view;
54 +
55 +
56 +
32 @Test 57 @Test
33 public void basics() throws IOException { 58 public void basics() throws IOException {
34 - UiExtension ext = new UiExtension(ImmutableList.of(new UiView(OTHER, "foo", "Foo View")), 59 + viewList = ImmutableList.of(FOO_VIEW);
35 - null, 60 + ext = new UiExtension.Builder(cl, viewList).build();
36 - getClass().getClassLoader()); 61 +
37 - String css = new String(toByteArray(ext.css())); 62 + css = new String(toByteArray(ext.css()));
38 assertTrue("incorrect css stream", css.contains("foo-css")); 63 assertTrue("incorrect css stream", css.contains("foo-css"));
39 - String js = new String(toByteArray(ext.js())); 64 + js = new String(toByteArray(ext.js()));
40 assertTrue("incorrect js stream", js.contains("foo-js")); 65 assertTrue("incorrect js stream", js.contains("foo-js"));
41 - assertEquals("incorrect view id", "foo", ext.views().get(0).id()); 66 +
42 - assertEquals("incorrect view category", OTHER, ext.views().get(0).category()); 67 + assertEquals("expected 1 view", 1, ext.views().size());
43 - assertNull("incorrect handler factory", ext.messageHandlerFactory()); 68 + view = ext.views().get(0);
69 + assertEquals("wrong view category", OTHER, view.category());
70 + assertEquals("wrong view id", FOO_ID, view.id());
71 + assertEquals("wrong view label", FOO_LABEL, view.label());
72 +
73 + assertNull("unexpected message handler factory", ext.messageHandlerFactory());
74 + assertNull("unexpected topo overlay factory", ext.topoOverlayFactory());
44 } 75 }
45 76
46 @Test 77 @Test
47 public void withPath() throws IOException { 78 public void withPath() throws IOException {
48 - UiExtension ext = new UiExtension(ImmutableList.of(new UiView(OTHER, "foo", "Foo View")), 79 + viewList = ImmutableList.of(FOO_VIEW);
49 - null, "custom", getClass().getClassLoader()); 80 + ext = new UiExtension.Builder(cl, viewList)
50 - String css = new String(toByteArray(ext.css())); 81 + .resourcePath(CUSTOM)
82 + .build();
83 +
84 + css = new String(toByteArray(ext.css()));
51 assertTrue("incorrect css stream", css.contains("custom-css")); 85 assertTrue("incorrect css stream", css.contains("custom-css"));
52 - String js = new String(toByteArray(ext.js())); 86 + js = new String(toByteArray(ext.js()));
53 assertTrue("incorrect js stream", js.contains("custom-js")); 87 assertTrue("incorrect js stream", js.contains("custom-js"));
54 - assertEquals("incorrect view id", "foo", ext.views().get(0).id()); 88 +
55 - assertNull("incorrect handler factory", ext.messageHandlerFactory()); 89 + assertEquals("expected 1 view", 1, ext.views().size());
90 + view = ext.views().get(0);
91 + assertEquals("wrong view category", OTHER, view.category());
92 + assertEquals("wrong view id", FOO_ID, view.id());
93 + assertEquals("wrong view label", FOO_LABEL, view.label());
94 +
95 + assertNull("unexpected message handler factory", ext.messageHandlerFactory());
96 + assertNull("unexpected topo overlay factory", ext.topoOverlayFactory());
97 + }
98 +
99 + @Test
100 + public void messageHandlerFactory() {
101 + viewList = ImmutableList.of(FOO_VIEW);
102 + ext = new UiExtension.Builder(cl, viewList)
103 + .messageHandlerFactory(MH_FACTORY)
104 + .build();
105 +
106 + assertEquals("wrong message handler factory", MH_FACTORY,
107 + ext.messageHandlerFactory());
108 + assertNull("unexpected topo overlay factory", ext.topoOverlayFactory());
109 + }
110 +
111 + @Test
112 + public void topoOverlayFactory() {
113 + viewList = ImmutableList.of(HIDDEN_VIEW);
114 + ext = new UiExtension.Builder(cl, viewList)
115 + .topoOverlayFactory(TO_FACTORY)
116 + .build();
117 +
118 + assertNull("unexpected message handler factory", ext.messageHandlerFactory());
119 + assertEquals("wrong topo overlay factory", TO_FACTORY,
120 + ext.topoOverlayFactory());
121 + }
122 +
123 + @Test
124 + public void twoViews() {
125 + viewList = ImmutableList.of(FOO_VIEW, BAR_VIEW);
126 + ext = new UiExtension.Builder(cl, viewList).build();
127 +
128 + assertEquals("expected 2 views", 2, ext.views().size());
129 +
130 + view = ext.views().get(0);
131 + assertEquals("wrong view category", OTHER, view.category());
132 + assertEquals("wrong view id", FOO_ID, view.id());
133 + assertEquals("wrong view label", FOO_LABEL, view.label());
134 +
135 + view = ext.views().get(1);
136 + assertEquals("wrong view category", OTHER, view.category());
137 + assertEquals("wrong view id", BAR_ID, view.id());
138 + assertEquals("wrong view label", BAR_LABEL, view.label());
56 } 139 }
57 } 140 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -57,8 +57,9 @@ public class AppUiComponent { ...@@ -57,8 +57,9 @@ public class AppUiComponent {
57 57
58 // Application UI extension 58 // Application UI extension
59 protected UiExtension extension = 59 protected UiExtension extension =
60 - new UiExtension(uiViews, messageHandlerFactory, 60 + new UiExtension.Builder(getClass().getClassLoader(), uiViews)
61 - getClass().getClassLoader()); 61 + .messageHandlerFactory(messageHandlerFactory)
62 + .build();
62 63
63 @Activate 64 @Activate
64 protected void activate() { 65 protected void activate() {
......
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;
19 +
20 +import org.onosproject.ui.UiTopoOverlay;
21 +
22 +import java.util.HashMap;
23 +import java.util.Map;
24 +
25 +/**
26 + * A cache of {@link org.onosproject.ui.UiTopoOverlay}'s that were registered
27 + * at the time the UI connection was established.
28 + */
29 +public class TopoOverlayCache {
30 +
31 + private final Map<String, UiTopoOverlay> overlays = new HashMap<>();
32 +
33 + /**
34 + * Adds a topology overlay to the cache.
35 + *
36 + * @param overlay a topology overlay
37 + */
38 + public void add(UiTopoOverlay overlay) {
39 + overlays.put(overlay.id(), overlay);
40 + }
41 +
42 + /**
43 + * Invoked when the cache is no longer needed.
44 + */
45 + public void destroy() {
46 + overlays.clear();
47 + }
48 +
49 + /**
50 + * Switching currently selected overlay.
51 + *
52 + * @param deact identity of overlay to deactivate
53 + * @param act identity of overlay to activate
54 + */
55 + public void switchOverlay(String deact, String act) {
56 + UiTopoOverlay toDeactivate = getOverlay(deact);
57 + UiTopoOverlay toActivate = getOverlay(act);
58 + if (toDeactivate != null) {
59 + toDeactivate.deactivate();
60 + }
61 + if (toActivate != null) {
62 + toActivate.activate();
63 + }
64 + }
65 +
66 + private UiTopoOverlay getOverlay(String id) {
67 + return id == null ? null : overlays.get(id);
68 + }
69 +
70 + /**
71 + * Returns the number of overlays in the cache.
72 + *
73 + * @return number of overlays
74 + */
75 + public int size() {
76 + return overlays.size();
77 + }
78 +}
...@@ -103,6 +103,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -103,6 +103,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
103 private static final String SPRITE_DATA_REQ = "spriteDataRequest"; 103 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
104 private static final String TOPO_START = "topoStart"; 104 private static final String TOPO_START = "topoStart";
105 private static final String TOPO_HEARTBEAT = "topoHeartbeat"; 105 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
106 + private static final String TOPO_SELECT_OVERLAY = "topoSelectOverlay";
106 private static final String TOPO_STOP = "topoStop"; 107 private static final String TOPO_STOP = "topoStop";
107 108
108 109
...@@ -135,6 +136,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -135,6 +136,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
135 private final ExecutorService msgSender = 136 private final ExecutorService msgSender =
136 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender")); 137 newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
137 138
139 + private TopoOverlayCache overlayCache;
140 +
138 private TimerTask trafficTask = null; 141 private TimerTask trafficTask = null;
139 private TrafficEvent trafficEvent = null; 142 private TrafficEvent trafficEvent = null;
140 143
...@@ -172,6 +175,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -172,6 +175,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
172 return ImmutableSet.of( 175 return ImmutableSet.of(
173 new TopoStart(), 176 new TopoStart(),
174 new TopoHeartbeat(), 177 new TopoHeartbeat(),
178 + new TopoSelectOverlay(),
175 new TopoStop(), 179 new TopoStop(),
176 new ReqSummary(), 180 new ReqSummary(),
177 new CancelSummary(), 181 new CancelSummary(),
...@@ -195,6 +199,15 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -195,6 +199,15 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
195 ); 199 );
196 } 200 }
197 201
202 + /**
203 + * Injects the topology overlay cache.
204 + *
205 + * @param overlayCache injected cache
206 + */
207 + void setOverlayCache(TopoOverlayCache overlayCache) {
208 + this.overlayCache = overlayCache;
209 + }
210 +
198 // ================================================================== 211 // ==================================================================
199 212
200 /** 213 /**
...@@ -231,6 +244,19 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -231,6 +244,19 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
231 } 244 }
232 } 245 }
233 246
247 + private final class TopoSelectOverlay extends RequestHandler {
248 + private TopoSelectOverlay() {
249 + super(TOPO_SELECT_OVERLAY);
250 + }
251 +
252 + @Override
253 + public void process(long sid, ObjectNode payload) {
254 + String deact = string(payload, "deactivate");
255 + String act = string(payload, "activate");
256 + overlayCache.switchOverlay(deact, act);
257 + }
258 + }
259 +
234 /** 260 /**
235 * @deprecated in Cardinal Release 261 * @deprecated in Cardinal Release
236 */ 262 */
...@@ -311,7 +337,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -311,7 +337,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
311 @Override 337 @Override
312 public void process(long sid, ObjectNode payload) { 338 public void process(long sid, ObjectNode payload) {
313 String type = string(payload, "class", "unknown"); 339 String type = string(payload, "class", "unknown");
314 - String id = JsonUtils.string(payload, "id"); 340 + String id = string(payload, "id");
315 341
316 if (type.equals("device")) { 342 if (type.equals("device")) {
317 sendMessage(deviceDetails(deviceId(id), sid)); 343 sendMessage(deviceDetails(deviceId(id), sid));
......
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;
19 +
20 +import org.onosproject.ui.UiTopoOverlay;
21 +
22 +/**
23 + * Topology Overlay for network traffic.
24 + */
25 +public class TrafficOverlay extends UiTopoOverlay {
26 + private static final String TRAFFIC_ID = "traffic";
27 +
28 + /**
29 + * Constructs the traffic overlay.
30 + */
31 + public TrafficOverlay() {
32 + super(TRAFFIC_ID);
33 + }
34 +
35 + // TODO : override init(), activate(), deactivate(), destroy()
36 +}
...@@ -30,6 +30,7 @@ import org.onosproject.mastership.MastershipService; ...@@ -30,6 +30,7 @@ import org.onosproject.mastership.MastershipService;
30 import org.onosproject.ui.UiExtension; 30 import org.onosproject.ui.UiExtension;
31 import org.onosproject.ui.UiExtensionService; 31 import org.onosproject.ui.UiExtensionService;
32 import org.onosproject.ui.UiMessageHandlerFactory; 32 import org.onosproject.ui.UiMessageHandlerFactory;
33 +import org.onosproject.ui.UiTopoOverlayFactory;
33 import org.onosproject.ui.UiView; 34 import org.onosproject.ui.UiView;
34 import org.onosproject.ui.UiViewHidden; 35 import org.onosproject.ui.UiViewHidden;
35 import org.onosproject.ui.impl.topo.OverlayService; 36 import org.onosproject.ui.impl.topo.OverlayService;
...@@ -54,6 +55,10 @@ import static org.onosproject.ui.UiView.Category.PLATFORM; ...@@ -54,6 +55,10 @@ import static org.onosproject.ui.UiView.Category.PLATFORM;
54 public class UiExtensionManager 55 public class UiExtensionManager
55 implements UiExtensionService, SpriteService, OverlayService { 56 implements UiExtensionService, SpriteService, OverlayService {
56 57
58 + private static final ClassLoader CL =
59 + UiExtensionManager.class.getClassLoader();
60 + private static final String CORE = "core";
61 +
57 private final Logger log = LoggerFactory.getLogger(getClass()); 62 private final Logger log = LoggerFactory.getLogger(getClass());
58 63
59 // List of all extensions 64 // List of all extensions
...@@ -104,8 +109,16 @@ public class UiExtensionManager ...@@ -104,8 +109,16 @@ public class UiExtensionManager
104 new ClusterViewMessageHandler() 109 new ClusterViewMessageHandler()
105 ); 110 );
106 111
107 - return new UiExtension(coreViews, messageHandlerFactory, "core", 112 + UiTopoOverlayFactory topoOverlayFactory =
108 - UiExtensionManager.class.getClassLoader()); 113 + () -> ImmutableList.of(
114 + new TrafficOverlay()
115 + );
116 +
117 + return new UiExtension.Builder(CL, coreViews)
118 + .messageHandlerFactory(messageHandlerFactory)
119 + .topoOverlayFactory(topoOverlayFactory)
120 + .resourcePath(CORE)
121 + .build();
109 } 122 }
110 123
111 @Activate 124 @Activate
......
...@@ -27,6 +27,7 @@ import org.onosproject.ui.UiConnection; ...@@ -27,6 +27,7 @@ import org.onosproject.ui.UiConnection;
27 import org.onosproject.ui.UiExtensionService; 27 import org.onosproject.ui.UiExtensionService;
28 import org.onosproject.ui.UiMessageHandlerFactory; 28 import org.onosproject.ui.UiMessageHandlerFactory;
29 import org.onosproject.ui.UiMessageHandler; 29 import org.onosproject.ui.UiMessageHandler;
30 +import org.onosproject.ui.UiTopoOverlayFactory;
30 import org.slf4j.Logger; 31 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory; 32 import org.slf4j.LoggerFactory;
32 33
...@@ -58,6 +59,7 @@ public class UiWebSocket ...@@ -58,6 +59,7 @@ public class UiWebSocket
58 private long lastActive = System.currentTimeMillis(); 59 private long lastActive = System.currentTimeMillis();
59 60
60 private Map<String, UiMessageHandler> handlers; 61 private Map<String, UiMessageHandler> handlers;
62 + private TopoOverlayCache overlayCache;
61 63
62 /** 64 /**
63 * Creates a new web-socket for serving data to GUI. 65 * Creates a new web-socket for serving data to GUI.
...@@ -72,7 +74,7 @@ public class UiWebSocket ...@@ -72,7 +74,7 @@ public class UiWebSocket
72 * Issues a close on the connection. 74 * Issues a close on the connection.
73 */ 75 */
74 synchronized void close() { 76 synchronized void close() {
75 - destroyHandlers(); 77 + destroyHandlersAndOverlays();
76 if (connection.isOpen()) { 78 if (connection.isOpen()) {
77 connection.close(); 79 connection.close();
78 } 80 }
...@@ -104,7 +106,7 @@ public class UiWebSocket ...@@ -104,7 +106,7 @@ public class UiWebSocket
104 this.connection = connection; 106 this.connection = connection;
105 this.control = (FrameConnection) connection; 107 this.control = (FrameConnection) connection;
106 try { 108 try {
107 - createHandlers(); 109 + createHandlersAndOverlays();
108 sendInstanceData(); 110 sendInstanceData();
109 log.info("GUI client connected"); 111 log.info("GUI client connected");
110 112
...@@ -118,7 +120,7 @@ public class UiWebSocket ...@@ -118,7 +120,7 @@ public class UiWebSocket
118 120
119 @Override 121 @Override
120 public synchronized void onClose(int closeCode, String message) { 122 public synchronized void onClose(int closeCode, String message) {
121 - destroyHandlers(); 123 + destroyHandlersAndOverlays();
122 log.info("GUI client disconnected [close-code={}, message={}]", 124 log.info("GUI client disconnected [close-code={}, message={}]",
123 closeCode, message); 125 closeCode, message);
124 } 126 }
...@@ -131,6 +133,7 @@ public class UiWebSocket ...@@ -131,6 +133,7 @@ public class UiWebSocket
131 133
132 @Override 134 @Override
133 public void onMessage(String data) { 135 public void onMessage(String data) {
136 + log.debug("onMessage: {}", data);
134 lastActive = System.currentTimeMillis(); 137 lastActive = System.currentTimeMillis();
135 try { 138 try {
136 ObjectNode message = (ObjectNode) mapper.reader().readTree(data); 139 ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
...@@ -172,8 +175,11 @@ public class UiWebSocket ...@@ -172,8 +175,11 @@ public class UiWebSocket
172 } 175 }
173 176
174 // Creates new message handlers. 177 // Creates new message handlers.
175 - private synchronized void createHandlers() { 178 + private synchronized void createHandlersAndOverlays() {
179 + log.debug("creating handlers and overlays...");
176 handlers = new HashMap<>(); 180 handlers = new HashMap<>();
181 + overlayCache = new TopoOverlayCache();
182 +
177 UiExtensionService service = directory.get(UiExtensionService.class); 183 UiExtensionService service = directory.get(UiExtensionService.class);
178 service.getExtensions().forEach(ext -> { 184 service.getExtensions().forEach(ext -> {
179 UiMessageHandlerFactory factory = ext.messageHandlerFactory(); 185 UiMessageHandlerFactory factory = ext.messageHandlerFactory();
...@@ -181,15 +187,33 @@ public class UiWebSocket ...@@ -181,15 +187,33 @@ public class UiWebSocket
181 factory.newHandlers().forEach(handler -> { 187 factory.newHandlers().forEach(handler -> {
182 handler.init(this, directory); 188 handler.init(this, directory);
183 handler.messageTypes().forEach(type -> handlers.put(type, handler)); 189 handler.messageTypes().forEach(type -> handlers.put(type, handler));
190 +
191 + // need to inject the overlay cache into topology message handler
192 + if (handler instanceof TopologyViewMessageHandler) {
193 + ((TopologyViewMessageHandler) handler).setOverlayCache(overlayCache);
194 + }
184 }); 195 });
185 } 196 }
197 +
198 + UiTopoOverlayFactory overlayFactory = ext.topoOverlayFactory();
199 + if (overlayFactory != null) {
200 + overlayFactory.newOverlays().forEach(overlayCache::add);
201 + }
186 }); 202 });
203 + log.debug("#handlers = {}, #overlays = {}", handlers.size(),
204 + overlayCache.size());
187 } 205 }
188 206
189 // Destroys message handlers. 207 // Destroys message handlers.
190 - private synchronized void destroyHandlers() { 208 + private synchronized void destroyHandlersAndOverlays() {
209 + log.debug("destroying handlers and overlays...");
191 handlers.forEach((type, handler) -> handler.destroy()); 210 handlers.forEach((type, handler) -> handler.destroy());
192 handlers.clear(); 211 handlers.clear();
212 +
213 + if (overlayCache != null) {
214 + overlayCache.destroy();
215 + overlayCache = null;
216 + }
193 } 217 }
194 218
195 // Sends cluster node/instance information to allow GUI to fail-over. 219 // Sends cluster node/instance information to allow GUI to fail-over.
......
...@@ -30,10 +30,11 @@ ...@@ -30,10 +30,11 @@
30 var tos = 'TopoOverlayService: '; 30 var tos = 'TopoOverlayService: ';
31 31
32 // injected refs 32 // injected refs
33 - var $log, fs, gs; 33 + var $log, fs, gs, wss;
34 34
35 // internal state 35 // internal state
36 - var overlays = {}; 36 + var overlays = {},
37 + current = null;
37 38
38 function error(fn, msg) { 39 function error(fn, msg) {
39 $log.error(tos + fn + '(): ' + msg); 40 $log.error(tos + fn + '(): ' + msg);
...@@ -108,20 +109,42 @@ ...@@ -108,20 +109,42 @@
108 return overlays[id]; 109 return overlays[id];
109 } 110 }
110 111
112 + // an overlay was selected via toolbar radio button press from user
113 + function tbSelection(id) {
114 + var same = current && current.overlayId === id,
115 + payload = {};
116 +
117 + function doop(op) {
118 + var oid = current.overlayId;
119 + $log.debug('Overlay:', op, oid);
120 + current[op]();
121 + payload[op] = oid;
122 + }
123 +
124 + if (!same) {
125 + current && doop('deactivate');
126 + current = overlay(id);
127 + current && doop('activate');
128 + wss.sendEvent('topoSelectOverlay', payload);
129 + }
130 + }
131 +
111 angular.module('ovTopo') 132 angular.module('ovTopo')
112 .factory('TopoOverlayService', 133 .factory('TopoOverlayService',
113 - ['$log', 'FnService', 'GlyphService', 134 + ['$log', 'FnService', 'GlyphService', 'WebSocketService',
114 135
115 - function (_$log_, _fs_, _gs_) { 136 + function (_$log_, _fs_, _gs_, _wss_) {
116 $log = _$log_; 137 $log = _$log_;
117 fs = _fs_; 138 fs = _fs_;
118 gs = _gs_; 139 gs = _gs_;
140 + wss = _wss_;
119 141
120 return { 142 return {
121 register: register, 143 register: register,
122 unregister: unregister, 144 unregister: unregister,
123 list: list, 145 list: list,
124 - overlay: overlay 146 + overlay: overlay,
147 + tbSelection: tbSelection
125 } 148 }
126 }]); 149 }]);
127 150
......
...@@ -50,14 +50,14 @@ ...@@ -50,14 +50,14 @@
50 L: { id: 'cycleLabels-btn', gid: 'cycleLabels' }, 50 L: { id: 'cycleLabels-btn', gid: 'cycleLabels' },
51 R: { id: 'resetZoom-btn', gid: 'resetZoom' }, 51 R: { id: 'resetZoom-btn', gid: 'resetZoom' },
52 52
53 + E: { id: 'eqMaster-btn', gid: 'eqMaster' },
54 +
53 V: { id: 'relatedIntents-btn', gid: 'relatedIntents' }, 55 V: { id: 'relatedIntents-btn', gid: 'relatedIntents' },
54 leftArrow: { id: 'prevIntent-btn', gid: 'prevIntent' }, 56 leftArrow: { id: 'prevIntent-btn', gid: 'prevIntent' },
55 rightArrow: { id: 'nextIntent-btn', gid: 'nextIntent' }, 57 rightArrow: { id: 'nextIntent-btn', gid: 'nextIntent' },
56 W: { id: 'intentTraffic-btn', gid: 'intentTraffic' }, 58 W: { id: 'intentTraffic-btn', gid: 'intentTraffic' },
57 A: { id: 'allTraffic-btn', gid: 'allTraffic' }, 59 A: { id: 'allTraffic-btn', gid: 'allTraffic' },
58 - F: { id: 'flows-btn', gid: 'flows' }, 60 + F: { id: 'flows-btn', gid: 'flows' }
59 -
60 - E: { id: 'eqMaster-btn', gid: 'eqMaster' }
61 }; 61 };
62 62
63 // initial toggle state: default settings and tag to key mapping 63 // initial toggle state: default settings and tag to key mapping
...@@ -153,14 +153,6 @@ ...@@ -153,14 +153,6 @@
153 addButton('E'); 153 addButton('E');
154 } 154 }
155 155
156 - function setOverlay(overlayId) {
157 - if (!overlayId) {
158 - $log.debug('CLEAR overlay');
159 - } else {
160 - $log.debug('SET overlay', overlayId);
161 - }
162 - }
163 -
164 function addOverlays() { 156 function addOverlays() {
165 toolbar.addSeparator(); 157 toolbar.addSeparator();
166 158
...@@ -168,7 +160,9 @@ ...@@ -168,7 +160,9 @@
168 var rset = [{ 160 var rset = [{
169 gid: 'unknown', 161 gid: 'unknown',
170 tooltip: 'No Overlay', 162 tooltip: 'No Overlay',
171 - cb: function () { setOverlay(); } 163 + cb: function () {
164 + tov.tbSelection(null);
165 + }
172 }]; 166 }];
173 167
174 tov.list().forEach(function (key) { 168 tov.list().forEach(function (key) {
...@@ -177,7 +171,7 @@ ...@@ -177,7 +171,7 @@
177 gid: ov._glyphId, 171 gid: ov._glyphId,
178 tooltip: (ov.tooltip || '(no tooltip)'), 172 tooltip: (ov.tooltip || '(no tooltip)'),
179 cb: function () { 173 cb: function () {
180 - setOverlay(ov.overlayId); 174 + tov.tbSelection(ov.overlayId);
181 } 175 }
182 }); 176 });
183 }); 177 });
...@@ -186,6 +180,7 @@ ...@@ -186,6 +180,7 @@
186 } 180 }
187 181
188 // TODO: 3rd row needs to be swapped in/out based on selected overlay 182 // TODO: 3rd row needs to be swapped in/out based on selected overlay
183 + // NOTE: This particular row of buttons is for the traffic overlay
189 function addThirdRow() { 184 function addThirdRow() {
190 addButton('V'); 185 addButton('V');
191 addButton('leftArrow'); 186 addButton('leftArrow');
......