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
diff --git a/core/api/src/main/java/org/onosproject/ui/UiExtension.java b/core/api/src/main/java/org/onosproject/ui/UiExtension.java
index cc10506..c310858 100644
--- a/core/api/src/main/java/org/onosproject/ui/UiExtension.java
+++ b/core/api/src/main/java/org/onosproject/ui/UiExtension.java
@@ -15,61 +15,47 @@
*/
package org.onosproject.ui;
-import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.List;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* User interface extension.
*/
-public class UiExtension {
+public final class UiExtension {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String VIEW_PREFIX = "app/view/";
+ private static final String EMPTY = "";
+ private static final String SLASH = "/";
+ private static final String CSS_HTML = "css.html";
+ private static final String JS_HTML = "js.html";
- private final String prefix;
private final ClassLoader classLoader;
+ private final String resourcePath;
private final List<UiView> views;
private final UiMessageHandlerFactory messageHandlerFactory;
+ private final UiTopoOverlayFactory topoOverlayFactory;
- /**
- * Creates a user interface extension for loading CSS and JS injections
- * from {@code css.html} and {@code js.html} resources, respectively.
- *
- * @param views list of contributed views
- * @param messageHandlerFactory optional message handler factory
- * @param classLoader class-loader for user interface resources
- */
- public UiExtension(List<UiView> views,
- UiMessageHandlerFactory messageHandlerFactory,
- ClassLoader classLoader) {
- this(views, messageHandlerFactory, null, classLoader);
+
+ // private constructor - only the builder calls this
+ private UiExtension(ClassLoader cl, String path, List<UiView> views,
+ UiMessageHandlerFactory mhFactory,
+ UiTopoOverlayFactory toFactory) {
+ this.classLoader = cl;
+ this.resourcePath = path;
+ this.views = views;
+ this.messageHandlerFactory = mhFactory;
+ this.topoOverlayFactory = toFactory;
}
- /**
- * Creates a user interface extension using custom resource prefix. It
- * loads CSS and JS injections from {@code path/css.html} and
- * {@code prefix/js.html} resources, respectively.
- *
- * @param views list of user interface views
- * @param messageHandlerFactory optional message handler factory
- * @param path resource path prefix
- * @param classLoader class-loader for user interface resources
- */
- public UiExtension(List<UiView> views,
- UiMessageHandlerFactory messageHandlerFactory,
- String path, ClassLoader classLoader) {
- this.views = checkNotNull(ImmutableList.copyOf(views), "Views cannot be null");
- this.messageHandlerFactory = messageHandlerFactory;
- this.prefix = path != null ? (path + "/") : "";
- this.classLoader = checkNotNull(classLoader, "Class loader must be specified");
- }
/**
* Returns input stream containing CSS inclusion statements.
@@ -77,7 +63,7 @@
* @return CSS inclusion statements
*/
public InputStream css() {
- return getStream(prefix + "css.html");
+ return getStream(resourcePath + CSS_HTML);
}
/**
@@ -86,7 +72,7 @@
* @return JavaScript inclusion statements
*/
public InputStream js() {
- return getStream(prefix + "js.html");
+ return getStream(resourcePath + JS_HTML);
}
/**
@@ -106,18 +92,28 @@
* @return resource input stream
*/
public InputStream resource(String viewId, String path) {
- return getStream(VIEW_PREFIX + viewId + "/" + path);
+ return getStream(VIEW_PREFIX + viewId + SLASH + path);
}
/**
- * Returns message handler factory.
+ * Returns message handler factory, if one was defined.
*
- * @return message handlers
+ * @return message handler factory
*/
public UiMessageHandlerFactory messageHandlerFactory() {
return messageHandlerFactory;
}
+ /**
+ * Returns the topology overlay factory, if one was defined.
+ *
+ * @return topology overlay factory
+ */
+ public UiTopoOverlayFactory topoOverlayFactory() {
+ return topoOverlayFactory;
+ }
+
+
// Returns the resource input stream from the specified class-loader.
private InputStream getStream(String path) {
InputStream stream = classLoader.getResourceAsStream(path);
@@ -128,4 +124,76 @@
}
+ /**
+ * UI Extension Builder.
+ */
+ public static class Builder {
+ private ClassLoader classLoader;
+
+ private String resourcePath = EMPTY;
+ private List<UiView> views = new ArrayList<>();
+ private UiMessageHandlerFactory messageHandlerFactory = null;
+ private UiTopoOverlayFactory topoOverlayFactory = null;
+
+ /**
+ * Create a builder with the given class loader.
+ * Resource path defaults to "".
+ * Views defaults to an empty list.
+ * Both Message and TopoOverlay factories default to null.
+ *
+ * @param cl the classloader
+ */
+ public Builder(ClassLoader cl, List<UiView> views) {
+ checkNotNull(cl, "Must provide a class loader");
+ checkArgument(views.size() > 0, "Must provide at least one view");
+ this.classLoader = cl;
+ this.views = views;
+ }
+
+ /**
+ * Set the resource path. That is, path to where the CSS and JS
+ * files are located. This value should
+ *
+ * @param path resource path
+ * @return self, for chaining
+ */
+ public Builder resourcePath(String path) {
+ this.resourcePath = path == null ? EMPTY : path + SLASH;
+ return this;
+ }
+
+ /**
+ * Sets the message handler factory for this extension.
+ *
+ * @param mhFactory message handler factory
+ * @return self, for chaining
+ */
+ public Builder messageHandlerFactory(UiMessageHandlerFactory mhFactory) {
+ this.messageHandlerFactory = mhFactory;
+ return this;
+ }
+
+ /**
+ * Sets the topology overlay factory for this extension.
+ *
+ * @param toFactory topology overlay factory
+ * @return self, for chaining
+ */
+ public Builder topoOverlayFactory(UiTopoOverlayFactory toFactory) {
+ this.topoOverlayFactory = toFactory;
+ return this;
+ }
+
+ /**
+ * Builds the UI extension.
+ *
+ * @return UI extension instance
+ */
+ public UiExtension build() {
+ return new UiExtension(classLoader, resourcePath, views,
+ messageHandlerFactory, topoOverlayFactory);
+ }
+
+ }
+
}
diff --git a/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java b/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java
index b3782a2..e27f5d9 100644
--- a/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java
+++ b/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java
@@ -107,8 +107,9 @@
void exec(String eventType, long sid, ObjectNode payload) {
RequestHandler requestHandler = handlerMap.get(eventType);
if (requestHandler != null) {
- log.debug("process {} event...", eventType);
requestHandler.process(sid, payload);
+ } else {
+ log.warn("no request handler for event type {}", eventType);
}
}
diff --git a/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java b/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java
new file mode 100644
index 0000000..999067a
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.ui;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Represents user interface topology view overlay.
+ */
+public class UiTopoOverlay {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final String id;
+
+ /**
+ * Creates a new user interface topology view overlay descriptor.
+ *
+ * @param id overlay identifier
+ */
+ public UiTopoOverlay(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the identifier for this overlay.
+ *
+ * @return the identifier
+ */
+ public String id() {
+ return id;
+ }
+
+ /**
+ * Callback invoked to initialize this overlay, soon after creation.
+ * This default implementation does nothing.
+ */
+ public void init() {
+ }
+
+ /**
+ * Callback invoked when this overlay is activated.
+ */
+ public void activate() {
+ log.debug("Overlay '{}' Activated", id);
+ }
+
+ /**
+ * Callback invoked when this overlay is deactivated.
+ */
+ public void deactivate() {
+ log.debug("Overlay '{}' Deactivated", id);
+ }
+
+ /**
+ * Callback invoked to destroy this instance by cleaning up any
+ * internal state ready for garbage collection.
+ * This default implementation does nothing.
+ */
+ public void destroy() {
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/UiTopoOverlayFactory.java b/core/api/src/main/java/org/onosproject/ui/UiTopoOverlayFactory.java
new file mode 100644
index 0000000..bd2f2fe
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/UiTopoOverlayFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.onosproject.ui;
+
+import java.util.Collection;
+
+/**
+ * Abstraction of an entity capable of producing one or more topology
+ * overlay handlers specific to a given user interface connection.
+ */
+public interface UiTopoOverlayFactory {
+
+ /**
+ * Produces a collection of new overlay handlers.
+ *
+ * @return collection of new overlay handlers
+ */
+ Collection<UiTopoOverlay> newOverlays();
+}
diff --git a/core/api/src/test/java/org/onosproject/ui/UiExtensionTest.java b/core/api/src/test/java/org/onosproject/ui/UiExtensionTest.java
index efc0261..bd3d15f 100644
--- a/core/api/src/test/java/org/onosproject/ui/UiExtensionTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/UiExtensionTest.java
@@ -19,6 +19,7 @@
import org.junit.Test;
import java.io.IOException;
+import java.util.List;
import static com.google.common.io.ByteStreams.toByteArray;
import static org.junit.Assert.*;
@@ -29,29 +30,111 @@
*/
public class UiExtensionTest {
+ private static final String FOO_ID = "foo";
+ private static final String FOO_LABEL = "Foo View";
+ private static final String BAR_ID = "bar";
+ private static final String BAR_LABEL = "Bar View";
+ private static final String CUSTOM = "custom";
+
+
+ private static final UiView FOO_VIEW = new UiView(OTHER, FOO_ID, FOO_LABEL);
+ private static final UiView BAR_VIEW = new UiView(OTHER, BAR_ID, BAR_LABEL);
+ private static final UiView HIDDEN_VIEW = new UiViewHidden(FOO_ID);
+
+ private static final UiMessageHandlerFactory MH_FACTORY = () -> null;
+ private static final UiTopoOverlayFactory TO_FACTORY = () -> null;
+
+ private final ClassLoader cl = getClass().getClassLoader();
+
+ private List<UiView> viewList;
+ private UiExtension ext;
+ private String css;
+ private String js;
+ private UiView view;
+
+
+
@Test
public void basics() throws IOException {
- UiExtension ext = new UiExtension(ImmutableList.of(new UiView(OTHER, "foo", "Foo View")),
- null,
- getClass().getClassLoader());
- String css = new String(toByteArray(ext.css()));
+ viewList = ImmutableList.of(FOO_VIEW);
+ ext = new UiExtension.Builder(cl, viewList).build();
+
+ css = new String(toByteArray(ext.css()));
assertTrue("incorrect css stream", css.contains("foo-css"));
- String js = new String(toByteArray(ext.js()));
+ js = new String(toByteArray(ext.js()));
assertTrue("incorrect js stream", js.contains("foo-js"));
- assertEquals("incorrect view id", "foo", ext.views().get(0).id());
- assertEquals("incorrect view category", OTHER, ext.views().get(0).category());
- assertNull("incorrect handler factory", ext.messageHandlerFactory());
+
+ assertEquals("expected 1 view", 1, ext.views().size());
+ view = ext.views().get(0);
+ assertEquals("wrong view category", OTHER, view.category());
+ assertEquals("wrong view id", FOO_ID, view.id());
+ assertEquals("wrong view label", FOO_LABEL, view.label());
+
+ assertNull("unexpected message handler factory", ext.messageHandlerFactory());
+ assertNull("unexpected topo overlay factory", ext.topoOverlayFactory());
}
@Test
public void withPath() throws IOException {
- UiExtension ext = new UiExtension(ImmutableList.of(new UiView(OTHER, "foo", "Foo View")),
- null, "custom", getClass().getClassLoader());
- String css = new String(toByteArray(ext.css()));
+ viewList = ImmutableList.of(FOO_VIEW);
+ ext = new UiExtension.Builder(cl, viewList)
+ .resourcePath(CUSTOM)
+ .build();
+
+ css = new String(toByteArray(ext.css()));
assertTrue("incorrect css stream", css.contains("custom-css"));
- String js = new String(toByteArray(ext.js()));
+ js = new String(toByteArray(ext.js()));
assertTrue("incorrect js stream", js.contains("custom-js"));
- assertEquals("incorrect view id", "foo", ext.views().get(0).id());
- assertNull("incorrect handler factory", ext.messageHandlerFactory());
+
+ assertEquals("expected 1 view", 1, ext.views().size());
+ view = ext.views().get(0);
+ assertEquals("wrong view category", OTHER, view.category());
+ assertEquals("wrong view id", FOO_ID, view.id());
+ assertEquals("wrong view label", FOO_LABEL, view.label());
+
+ assertNull("unexpected message handler factory", ext.messageHandlerFactory());
+ assertNull("unexpected topo overlay factory", ext.topoOverlayFactory());
+ }
+
+ @Test
+ public void messageHandlerFactory() {
+ viewList = ImmutableList.of(FOO_VIEW);
+ ext = new UiExtension.Builder(cl, viewList)
+ .messageHandlerFactory(MH_FACTORY)
+ .build();
+
+ assertEquals("wrong message handler factory", MH_FACTORY,
+ ext.messageHandlerFactory());
+ assertNull("unexpected topo overlay factory", ext.topoOverlayFactory());
+ }
+
+ @Test
+ public void topoOverlayFactory() {
+ viewList = ImmutableList.of(HIDDEN_VIEW);
+ ext = new UiExtension.Builder(cl, viewList)
+ .topoOverlayFactory(TO_FACTORY)
+ .build();
+
+ assertNull("unexpected message handler factory", ext.messageHandlerFactory());
+ assertEquals("wrong topo overlay factory", TO_FACTORY,
+ ext.topoOverlayFactory());
+ }
+
+ @Test
+ public void twoViews() {
+ viewList = ImmutableList.of(FOO_VIEW, BAR_VIEW);
+ ext = new UiExtension.Builder(cl, viewList).build();
+
+ assertEquals("expected 2 views", 2, ext.views().size());
+
+ view = ext.views().get(0);
+ assertEquals("wrong view category", OTHER, view.category());
+ assertEquals("wrong view id", FOO_ID, view.id());
+ assertEquals("wrong view label", FOO_LABEL, view.label());
+
+ view = ext.views().get(1);
+ assertEquals("wrong view category", OTHER, view.category());
+ assertEquals("wrong view id", BAR_ID, view.id());
+ assertEquals("wrong view label", BAR_LABEL, view.label());
}
}
\ No newline at end of file