ONOS-6258: UiTopo2Overlay et al.

- initial support for topo-2 highlighting.

Change-Id: I71c61b902047153ea420a8b2ecd89f6950daa4a9
This commit is contained in:
Simon Hunt 2017-04-26 17:28:42 -07:00
parent ac34890cac
commit 22c35df758
8 changed files with 457 additions and 7 deletions

View File

@ -24,6 +24,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.ui.UiExtension; import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService; import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory; import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiTopo2OverlayFactory;
import org.onosproject.ui.UiView; import org.onosproject.ui.UiView;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -56,11 +57,22 @@ public class DriverViewComponent {
new DriverViewMessageHandler() new DriverViewMessageHandler()
); );
// ++++ ====================================================== ++++
// ++++ Temporary code for testing the topology-2 overlay code ++++
private final UiTopo2OverlayFactory t2ovFactory =
() -> ImmutableList.of(
new TesterTopo2Overlay()
);
// ++++ ====================================================== ++++
// Application UI extension // Application UI extension
protected UiExtension extension = protected UiExtension extension =
new UiExtension.Builder(getClass().getClassLoader(), uiViews) new UiExtension.Builder(getClass().getClassLoader(), uiViews)
.resourcePath(VIEW_ID) .resourcePath(VIEW_ID)
.messageHandlerFactory(messageHandlerFactory) .messageHandlerFactory(messageHandlerFactory)
.topo2OverlayFactory(t2ovFactory) // +++ TEMP +++
.build(); .build();
@Activate @Activate

View File

@ -0,0 +1,53 @@
/*
* Copyright 2017-present 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.drivermatrix;
import org.onosproject.ui.UiTopo2Overlay;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A test implementation of UiTopo2Overlay.
*/
public class TesterTopo2Overlay extends UiTopo2Overlay {
private final Logger log = LoggerFactory.getLogger(getClass());
// NOTE: this must match the ID defined in dmatrixTopo2v.js
private static final String OVERLAY_ID = "dmatrix-test-overlay";
private static final String NAME = "Test D-Matrix Overlay";
/**
* Constructs the overlay.
*/
public TesterTopo2Overlay() {
super(OVERLAY_ID, NAME);
log.debug("+++ CREATE +++ TesterTopo2Overlay");
}
@Override
public String glyphId() {
return "thatsNoMoon";
}
@Override
public void highlightingCallback() {
// TODO: figure out what API to use to set highlights....
}
}

View File

@ -47,6 +47,7 @@ public final class UiExtension {
private final List<UiView> viewList; private final List<UiView> viewList;
private final UiMessageHandlerFactory messageHandlerFactory; private final UiMessageHandlerFactory messageHandlerFactory;
private final UiTopoOverlayFactory topoOverlayFactory; private final UiTopoOverlayFactory topoOverlayFactory;
private final UiTopo2OverlayFactory topo2OverlayFactory;
private final UiTopoMapFactory topoMapFactory; private final UiTopoMapFactory topoMapFactory;
private boolean isValid = true; private boolean isValid = true;
@ -55,12 +56,14 @@ public final class UiExtension {
private UiExtension(ClassLoader cl, String path, List<UiView> views, private UiExtension(ClassLoader cl, String path, List<UiView> views,
UiMessageHandlerFactory mhFactory, UiMessageHandlerFactory mhFactory,
UiTopoOverlayFactory toFactory, UiTopoOverlayFactory toFactory,
UiTopo2OverlayFactory to2Factory,
UiTopoMapFactory tmFactory) { UiTopoMapFactory tmFactory) {
classLoader = cl; classLoader = cl;
resourcePath = path; resourcePath = path;
viewList = views; viewList = views;
messageHandlerFactory = mhFactory; messageHandlerFactory = mhFactory;
topoOverlayFactory = toFactory; topoOverlayFactory = toFactory;
topo2OverlayFactory = to2Factory;
topoMapFactory = tmFactory; topoMapFactory = tmFactory;
} }
@ -121,6 +124,15 @@ public final class UiExtension {
return topoOverlayFactory; return topoOverlayFactory;
} }
/**
* Returns the topology-2 overlay factory, if one was defined.
*
* @return topology-2 overlay factory
*/
public UiTopo2OverlayFactory topo2OverlayFactory() {
return topo2OverlayFactory;
}
/** /**
* Returns the topology map factory, if one was defined. * Returns the topology map factory, if one was defined.
* *
@ -152,6 +164,7 @@ public final class UiExtension {
private List<UiView> viewList = new ArrayList<>(); private List<UiView> viewList = new ArrayList<>();
private UiMessageHandlerFactory messageHandlerFactory = null; private UiMessageHandlerFactory messageHandlerFactory = null;
private UiTopoOverlayFactory topoOverlayFactory = null; private UiTopoOverlayFactory topoOverlayFactory = null;
private UiTopo2OverlayFactory topo2OverlayFactory = null;
private UiTopoMapFactory topoMapFactory = null; private UiTopoMapFactory topoMapFactory = null;
/** /**
@ -204,6 +217,17 @@ public final class UiExtension {
return this; return this;
} }
/**
* Sets the topology-2 overlay factory for this extension.
*
* @param to2Factory topology-2 overlay factory
* @return self, for chaining
*/
public Builder topo2OverlayFactory(UiTopo2OverlayFactory to2Factory) {
topo2OverlayFactory = to2Factory;
return this;
}
/** /**
* Sets the topology map factory for this extension. * Sets the topology map factory for this extension.
* *
@ -222,8 +246,8 @@ public final class UiExtension {
*/ */
public UiExtension build() { public UiExtension build() {
return new UiExtension(classLoader, resourcePath, viewList, return new UiExtension(classLoader, resourcePath, viewList,
messageHandlerFactory, topoOverlayFactory, messageHandlerFactory, topoOverlayFactory,
topoMapFactory); topo2OverlayFactory, topoMapFactory);
} }
} }
} }

View File

@ -0,0 +1,132 @@
/*
* Copyright 2017-present 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 a user interface topology-2 view overlay.
* <p>
* This base class does little more than provide a logger, an identifier,
* name, and glyph ID.
* Subclasses will probably want to override some or all of the base methods
* to do useful things during the life-cycle of the (topo-2) overlay.
*/
public class UiTopo2Overlay {
private static final String DEFAULT_GLYPH_ID = "m_topo";
/**
* Logger for this overlay.
*/
protected final Logger log = LoggerFactory.getLogger(getClass());
private final String id;
private final String name;
private boolean isActive = false;
/**
* Creates a new user interface topology view overlay descriptor, with
* the given identifier and (human readable) name.
*
* @param id overlay identifier
* @param name overlay name
*/
public UiTopo2Overlay(String id, String name) {
this.id = id;
this.name = name;
}
/**
* Returns the identifier for this overlay.
*
* @return the identifier
*/
public String id() {
return id;
}
/**
* Returns the name for this overlay.
*
* @return the name
*/
public String name() {
return name;
}
/**
* Returns the glyph identifier to use in the toolbar.
* This implementation returns a default value. Subclasses may override
* this to provide the identity of a custom glyph.
*
* @return glyph ID
*/
public String glyphId() {
return DEFAULT_GLYPH_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() {
isActive = true;
}
/**
* Callback invoked when this overlay is deactivated.
*/
public void deactivate() {
isActive = false;
}
/**
* Returns true if this overlay is currently active.
*
* @return true if overlay active
*/
public boolean isActive() {
return isActive;
}
/**
* Callback invoked to destroy this instance by cleaning up any
* internal state ready for garbage collection.
* This default implementation holds no state and does nothing.
*/
public void destroy() {
}
/**
* Callback invoked when the topology highlighting should be updated.
* It is the implementation's responsibility to update the Model
* Highlighter state. This implementation does nothing.
*/
public void highlightingCallback(/* ref to highlight model ? */) {
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2017-present 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-2
* overlay handlers specific to a given user interface connection.
*/
public interface UiTopo2OverlayFactory {
/**
* Produces a collection of new overlay handlers for topology-2 view.
*
* @return collection of new overlay handlers
*/
Collection<UiTopo2Overlay> newOverlays();
}

View File

@ -28,9 +28,12 @@ import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiExtensionService; import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandler; import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.UiMessageHandlerFactory; import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiTopo2OverlayFactory;
import org.onosproject.ui.UiTopoLayoutService; import org.onosproject.ui.UiTopoLayoutService;
import org.onosproject.ui.UiTopoOverlayFactory; import org.onosproject.ui.UiTopoOverlayFactory;
import org.onosproject.ui.impl.topo.Topo2Jsonifier; import org.onosproject.ui.impl.topo.Topo2Jsonifier;
import org.onosproject.ui.impl.topo.Topo2OverlayCache;
import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
import org.onosproject.ui.impl.topo.UiTopoSession; import org.onosproject.ui.impl.topo.UiTopoSession;
import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel; import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
import org.onosproject.ui.model.topo.UiTopoLayout; import org.onosproject.ui.model.topo.UiTopoLayout;
@ -50,7 +53,6 @@ public class UiWebSocket
private static final Logger log = LoggerFactory.getLogger(UiWebSocket.class); private static final Logger log = LoggerFactory.getLogger(UiWebSocket.class);
private static final String EVENT = "event"; private static final String EVENT = "event";
private static final String SID = "sid";
private static final String PAYLOAD = "payload"; private static final String PAYLOAD = "payload";
private static final String UNKNOWN = "unknown"; private static final String UNKNOWN = "unknown";
@ -81,6 +83,7 @@ public class UiWebSocket
private Map<String, UiMessageHandler> handlers; private Map<String, UiMessageHandler> handlers;
private TopoOverlayCache overlayCache; private TopoOverlayCache overlayCache;
private Topo2OverlayCache overlay2Cache;
/** /**
* Creates a new web-socket for serving data to the Web UI. * Creates a new web-socket for serving data to the Web UI.
@ -190,7 +193,7 @@ public class UiWebSocket
topoSession.destroy(); topoSession.destroy();
destroyHandlersAndOverlays(); destroyHandlersAndOverlays();
log.info("GUI client disconnected [close-code={}, message={}]", log.info("GUI client disconnected [close-code={}, message={}]",
closeCode, message); closeCode, message);
} }
@Override @Override
@ -244,6 +247,7 @@ public class UiWebSocket
log.debug("Creating handlers and overlays..."); log.debug("Creating handlers and overlays...");
handlers = new HashMap<>(); handlers = new HashMap<>();
overlayCache = new TopoOverlayCache(); overlayCache = new TopoOverlayCache();
overlay2Cache = new Topo2OverlayCache();
UiExtensionService service = directory.get(UiExtensionService.class); UiExtensionService service = directory.get(UiExtensionService.class);
service.getExtensions().forEach(ext -> { service.getExtensions().forEach(ext -> {
@ -255,10 +259,13 @@ public class UiWebSocket
handler.messageTypes().forEach(type -> handlers.put(type, handler)); handler.messageTypes().forEach(type -> handlers.put(type, handler));
// need to inject the overlay cache into topology message handler // need to inject the overlay cache into topology message handler
// TODO: code for Topo2ViewMessageHandler required here
if (handler instanceof TopologyViewMessageHandler) { if (handler instanceof TopologyViewMessageHandler) {
((TopologyViewMessageHandler) handler).setOverlayCache(overlayCache); ((TopologyViewMessageHandler) handler).setOverlayCache(overlayCache);
} }
if (handler instanceof Topo2ViewMessageHandler) {
((Topo2ViewMessageHandler) handler).setOverlayCache(overlay2Cache);
}
} catch (Exception e) { } catch (Exception e) {
log.warn("Unable to setup handler {} due to", handler, e); log.warn("Unable to setup handler {} due to", handler, e);
} }
@ -269,9 +276,14 @@ public class UiWebSocket
if (overlayFactory != null) { if (overlayFactory != null) {
overlayFactory.newOverlays().forEach(overlayCache::add); overlayFactory.newOverlays().forEach(overlayCache::add);
} }
UiTopo2OverlayFactory overlay2Factory = ext.topo2OverlayFactory();
if (overlay2Factory != null) {
overlay2Factory.newOverlays().forEach(overlay2Cache::add);
}
}); });
log.debug("#handlers = {}, #overlays = {}", handlers.size(), log.debug("#handlers = {}, #overlays = {}", handlers.size(),
overlayCache.size()); overlayCache.size());
} }
// Destroys message handlers. // Destroys message handlers.
@ -298,7 +310,7 @@ public class UiWebSocket
.put(ID, node.id().toString()) .put(ID, node.id().toString())
.put(IP, node.ip().toString()) .put(IP, node.ip().toString())
.put(GlyphConstants.UI_ATTACHED, .put(GlyphConstants.UI_ATTACHED,
node.equals(service.getLocalNode())); node.equals(service.getLocalNode()));
instances.add(instance); instances.add(instance);
} }

View File

@ -0,0 +1,146 @@
/*
* Copyright 2017-present 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.impl.topo;
import org.onosproject.ui.UiTopo2Overlay;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Strings.isNullOrEmpty;
/**
* A cache of {@link org.onosproject.ui.UiTopo2Overlay}'s that were
* registered at the time the UI connection was established.
* <p>
* Note, for now, this is a simplified version which will only cache
* a single overlay. At some future point, this should be expanded to mirror
* the behavior of {@link org.onosproject.ui.impl.TopoOverlayCache}.
*/
public class Topo2OverlayCache {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String EMPTY = "";
private static final String NO_OVERLAY = "No Overlay";
private static final String UNKNOWN = "unknown";
private static final UiTopo2Overlay NONE = new NullOverlay();
private final Map<String, UiTopo2Overlay> overlays = new HashMap<>();
private UiTopo2Overlay current = null;
/**
* Constructs the overlay cache.
*/
public Topo2OverlayCache() {
overlays.put(null, NONE);
}
/**
* Adds a topology-2 overlay to the cache.
*
* @param overlay a topology-2 overlay
*/
public void add(UiTopo2Overlay overlay) {
overlays.put(overlay.id(), overlay);
log.warn("added overlay: " + overlay);
}
/**
* Invoked when the cache is no longer needed.
*/
public void destroy() {
overlays.clear();
}
/**
* Switching currently selected overlay.
*
* @param deact identity of overlay to deactivate
* @param act identity of overlay to activate
*/
public void switchOverlay(String deact, String act) {
UiTopo2Overlay toDeactivate = getOverlay(deact);
UiTopo2Overlay toActivate = getOverlay(act);
toDeactivate.deactivate();
current = toActivate;
current.activate();
}
private UiTopo2Overlay getOverlay(String id) {
return isNullOrEmpty(id) ? NONE : overlays.get(id);
}
/**
* Returns the current overlay instance.
* Note that this method always returns a reference; when there is no
* overlay selected the "NULL" overlay instance is returned.
*
* @return the current overlay
*/
public UiTopo2Overlay currentOverlay() {
return current;
}
/**
* Returns the number of overlays in the cache. Remember that this
* includes the "NULL" overlay, representing "no overlay selected".
*
* @return number of overlays
*/
public int size() {
return overlays.size();
}
/**
* Returns true if the identifier of the currently active overlay
* matches the given parameter.
*
* @param overlayId overlay identifier
* @return true if this matches the ID of currently active overlay
*/
public boolean isActive(String overlayId) {
return currentOverlay().id().equals(overlayId);
}
/**
* Returns the collection of registered overlays.
*
* @return registered overlays
*/
public Collection<UiTopo2Overlay> list() {
return overlays.values();
}
// overlay instance representing "no overlay selected"
private static class NullOverlay extends UiTopo2Overlay {
NullOverlay() {
super(EMPTY, NO_OVERLAY);
}
@Override
public String glyphId() {
return UNKNOWN;
}
}
}

View File

@ -16,12 +16,14 @@
package org.onosproject.ui.impl.topo; package org.onosproject.ui.impl.topo;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import org.onlab.osgi.ServiceDirectory; import org.onlab.osgi.ServiceDirectory;
import org.onosproject.ui.RequestHandler; import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection; import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler; import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.UiTopo2Overlay;
import org.onosproject.ui.impl.UiWebSocket; import org.onosproject.ui.impl.UiWebSocket;
import org.onosproject.ui.model.topo.UiClusterMember; import org.onosproject.ui.model.topo.UiClusterMember;
import org.onosproject.ui.model.topo.UiNode; import org.onosproject.ui.model.topo.UiNode;
@ -67,10 +69,12 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
private static final String CURRENT_LAYOUT = "topo2CurrentLayout"; private static final String CURRENT_LAYOUT = "topo2CurrentLayout";
private static final String CURRENT_REGION = "topo2CurrentRegion"; private static final String CURRENT_REGION = "topo2CurrentRegion";
private static final String PEER_REGIONS = "topo2PeerRegions"; private static final String PEER_REGIONS = "topo2PeerRegions";
private static final String OVERLAYS = "topo2Overlays";
private UiTopoSession topoSession; private UiTopoSession topoSession;
private Topo2Jsonifier t2json; private Topo2Jsonifier t2json;
private Topo2OverlayCache overlay2Cache;
@Override @Override
@ -82,6 +86,17 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
t2json = new Topo2Jsonifier(directory, connection.userName()); t2json = new Topo2Jsonifier(directory, connection.userName());
} }
/**
* Sets a reference to the overlay cache for interacting with registered
* overlays.
*
* @param overlay2Cache the overlay cache
*/
public void setOverlayCache(Topo2OverlayCache overlay2Cache) {
this.overlay2Cache = overlay2Cache;
}
@Override @Override
protected Collection<RequestHandler> createRequestHandlers() { protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of( return ImmutableSet.of(
@ -114,6 +129,25 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
return peersPayload; return peersPayload;
} }
private ObjectNode mkOverlaysMessage() {
ArrayNode a = arrayNode();
for (UiTopo2Overlay ov : overlay2Cache.list()) {
a.add(json(ov));
}
ObjectNode payload = objectNode();
payload.set("overlays", a);
return payload;
}
private ObjectNode json(UiTopo2Overlay ov) {
return objectNode()
.put("id", ov.id())
.put("name", ov.name())
.put("gid", ov.glyphId());
}
// ==================================================================
private final class Topo2Start extends RequestHandler { private final class Topo2Start extends RequestHandler {
private Topo2Start() { private Topo2Start() {
@ -152,6 +186,9 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
// these are the regions/devices that are siblings to this region // these are the regions/devices that are siblings to this region
sendMessage(PEER_REGIONS, mkPeersMessage(currentLayout)); sendMessage(PEER_REGIONS, mkPeersMessage(currentLayout));
// these are the registered overlays
sendMessage(OVERLAYS, mkOverlaysMessage());
} }
} }