Simon Hunt

GUI -- Huge Refactoring of server-side message handlers (Part Two).

--- Well, it compiles, and seems to work, with the cursory testing I've done...

Change-Id: I0e59657c134e109850e4770766083370dfd9fdc2
......@@ -27,10 +27,11 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.intentperf.IntentPerfCollector.Sample;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.UiView;
import java.util.Collection;
......@@ -48,14 +49,20 @@ import static org.onosproject.ui.UiView.Category.OTHER;
@Service(value = IntentPerfUi.class)
public class IntentPerfUi {
private static final String INTENT_PERF_START = "intentPerfStart";
private static final String INTENT_PERF_STOP = "intentPerfStop";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected UiExtensionService uiExtensionService;
private final Set<StreamingControl> handlers = synchronizedSet(new HashSet<>());
private List<UiView> views = ImmutableList.of(new UiView(OTHER, "intentPerf", "Intent Performance"));
private UiExtension uiExtension = new UiExtension(views, this::newHandlers,
getClass().getClassLoader());
private List<UiView> views = ImmutableList.of(
new UiView(OTHER, "intentPerf", "Intent Performance")
);
private UiExtension uiExtension =
new UiExtension(views, this::newHandlers, getClass().getClassLoader());
private IntentPerfCollector collector;
......@@ -90,25 +97,22 @@ public class IntentPerfUi {
}
// Creates and returns session specific message handler.
private Collection<UiMessageHandler> newHandlers() {
private Collection<UiMessageHandlerTwo> newHandlers() {
return ImmutableList.of(new StreamingControl());
}
// UI Message handlers for turning on/off reporting to a session.
private class StreamingControl extends UiMessageHandler {
private class StreamingControl extends UiMessageHandlerTwo {
private boolean streamingEnabled = false;
protected StreamingControl() {
super(ImmutableSet.of("intentPerfStart", "intentPerfStop"));
}
@Override
public void process(ObjectNode message) {
streamingEnabled = message.path("event").asText("unknown").equals("intentPerfStart");
if (streamingEnabled) {
sendInitData();
}
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(
new IntentPerfStart(),
new IntentPerfStop()
);
}
@Override
......@@ -129,17 +133,6 @@ public class IntentPerfUi {
}
}
private void sendInitData() {
ObjectNode rootNode = mapper.createObjectNode();
ArrayNode an = mapper.createArrayNode();
ArrayNode sn = mapper.createArrayNode();
rootNode.set("headers", an);
rootNode.set("samples", sn);
collector.getSampleHeaders().forEach(an::add);
collector.getSamples().forEach(s -> sn.add(sampleNode(s)));
connection().sendMessage("intentPerfInit", 0, rootNode);
}
private ObjectNode sampleNode(Sample sample) {
ObjectNode sampleNode = mapper.createObjectNode();
......@@ -153,6 +146,47 @@ public class IntentPerfUi {
return sampleNode;
}
// ======================================================================
private final class IntentPerfStart extends RequestHandler {
private IntentPerfStart() {
super(INTENT_PERF_START);
}
@Override
public void process(long sid, ObjectNode payload) {
streamingEnabled = true;
sendInitData();
}
private void sendInitData() {
ObjectNode rootNode = MAPPER.createObjectNode();
ArrayNode an = MAPPER.createArrayNode();
ArrayNode sn = MAPPER.createArrayNode();
rootNode.set("headers", an);
rootNode.set("samples", sn);
collector.getSampleHeaders().forEach(an::add);
collector.getSamples().forEach(s -> sn.add(sampleNode(s)));
sendMessage("intentPerfInit", 0, rootNode);
}
}
// ======================================================================
private final class IntentPerfStop extends RequestHandler {
private IntentPerfStop() {
super(INTENT_PERF_STOP);
}
@Override
public void process(long sid, ObjectNode payload) {
streamingEnabled = false;
}
}
}
}
......
......@@ -61,6 +61,17 @@ public final class JsonUtils {
}
/**
* Returns the sequence identifier from the specified event, or 0 (zero)
* if the "sid" property does not exist.
*
* @param event message event
* @return extracted sequence identifier
*/
public static long sid(ObjectNode event) {
return number(event, "sid");
}
/**
* Returns the payload from the specified event.
*
* @param event message event
......@@ -95,7 +106,7 @@ public final class JsonUtils {
/**
* Returns the specified node property as a string, with a default fallback.
*
* @param node message event
* @param node object node
* @param name property name
* @param defaultValue fallback value if property is absent
* @return property as a string
......@@ -104,4 +115,15 @@ public final class JsonUtils {
return node.path(name).asText(defaultValue);
}
/**
* Returns the specified node property as an object node.
*
* @param node object node
* @param name property name
* @return property as a node
*/
public static ObjectNode node(ObjectNode node, String name) {
return (ObjectNode) node.path(name);
}
}
......
/*
* 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 com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Abstraction of an entity that handles a specific request from the
* user interface client.
*
* @see UiMessageHandlerTwo
*/
public abstract class RequestHandler {
protected static final ObjectMapper MAPPER = new ObjectMapper();
private final String eventType;
private UiMessageHandlerTwo parent;
public RequestHandler(String eventType) {
this.eventType = eventType;
}
// package private
void setParent(UiMessageHandlerTwo parent) {
this.parent = parent;
}
/**
* Returns the event type that this handler handles.
*
* @return event type
*/
public String eventType() {
return eventType;
}
/**
* Processes the incoming message payload from the client.
*
* @param sid message sequence identifier
* @param payload request message payload
*/
public abstract void process(long sid, ObjectNode payload);
// ===================================================================
// === Convenience methods...
/**
* Returns implementation of the specified service class.
*
* @param serviceClass service class
* @param <T> type of service
* @return implementation class
* @throws org.onlab.osgi.ServiceNotFoundException if no implementation found
*/
protected <T> T get(Class<T> serviceClass) {
return parent.directory().get(serviceClass);
}
/**
* Sends a message back to the client.
*
* @param eventType message event type
* @param sid message sequence identifier
* @param payload message payload
*/
protected void sendMessage(String eventType, long sid, ObjectNode payload) {
parent.connection().sendMessage(eventType, sid, payload);
}
/**
* Sends a message back to the client.
* Here, the message is preformatted; the assumption is it has its
* eventType, sid and payload attributes already filled in.
*
* @param message the message to send
*/
protected void sendMessage(ObjectNode message) {
parent.connection().sendMessage(message);
}
/**
* Allows one request handler to pass the event on to another for
* further processing.
* Note that the message handlers must be defined in the same parent.
*
* @param eventType event type
* @param sid sequence identifier
* @param payload message payload
*/
protected void chain(String eventType, long sid, ObjectNode payload) {
parent.exec(eventType, sid, payload);
}
// ===================================================================
// FIXME : Javadocs
protected String string(ObjectNode node, String key) {
return JsonUtils.string(node, key);
}
protected String string(ObjectNode node, String key, String defValue) {
return JsonUtils.string(node, key, defValue);
}
}
......@@ -40,6 +40,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* }
* </pre>
*/
@Deprecated
public abstract class UiMessageHandler {
private final Set<String> messageTypes;
......
......@@ -28,6 +28,6 @@ public interface UiMessageHandlerFactory {
*
* @return collection of new handlers
*/
Collection<UiMessageHandler> newHandlers();
Collection<UiMessageHandlerTwo> newHandlers();
}
......
/*
* 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 com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.osgi.ServiceDirectory;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of an entity capable of processing a JSON message from the user
* interface client.
* <p>
* The message is a JSON object with the following structure:
* </p>
* <pre>
* {
* "type": "<em>event-type</em>",
* "sid": "<em>sequence-number</em>",
* "payload": {
* <em>arbitrary JSON object structure</em>
* }
* }
* </pre>
*/
public abstract class UiMessageHandlerTwo {
private final Map<String, RequestHandler> handlerMap = new HashMap<>();
private UiConnection connection;
private ServiceDirectory directory;
/**
* Mapper for creating ObjectNodes and ArrayNodes etc.
*/
protected final ObjectMapper mapper = new ObjectMapper();
/**
* Binds the handlers returned from {@link #getHandlers()} to this
* instance.
*/
void bindHandlers() {
Collection<RequestHandler> handlers = getHandlers();
checkNotNull(handlers, "Handlers cannot be null");
checkArgument(!handlers.isEmpty(), "Handlers cannot be empty");
for (RequestHandler h : handlers) {
h.setParent(this);
handlerMap.put(h.eventType(), h);
}
}
/**
* Subclasses must return the collection of handlers for the
* message types they handle.
*
* @return the message handler instances
*/
protected abstract Collection<RequestHandler> getHandlers();
/**
* Returns the set of message types which this handler is capable of
* processing.
*
* @return set of message types
*/
public Set<String> messageTypes() {
return Collections.unmodifiableSet(handlerMap.keySet());
}
/**
* Processes a JSON message from the user interface client.
*
* @param message JSON message
*/
public void process(ObjectNode message) {
String type = JsonUtils.eventType(message);
long sid = JsonUtils.sid(message);
ObjectNode payload = JsonUtils.payload(message);
exec(type, sid, payload);
}
/**
* Finds the appropriate handler and executes the process method.
*
* @param eventType event type
* @param sid sequence identifier
* @param payload message payload
*/
void exec(String eventType, long sid, ObjectNode payload) {
RequestHandler handler = handlerMap.get(eventType);
if (handler != null) {
handler.process(sid, payload);
}
}
/**
* Initializes the handler with the user interface connection and
* service directory context.
*
* @param connection user interface connection
* @param directory service directory
*/
public void init(UiConnection connection, ServiceDirectory directory) {
this.connection = connection;
this.directory = directory;
bindHandlers();
}
/**
* Destroys the message handler context.
*/
public void destroy() {
this.connection = null;
this.directory = null;
}
/**
* Returns the user interface connection with which this handler was primed.
*
* @return user interface connection
*/
public UiConnection connection() {
return connection;
}
/**
* Returns the user interface connection with which this handler was primed.
*
* @return user interface connection
*/
public ServiceDirectory directory() {
return directory;
}
/**
* Returns implementation of the specified service class.
*
* @param serviceClass service class
* @param <T> type of service
* @return implementation class
* @throws org.onlab.osgi.ServiceNotFoundException if no implementation found
*/
protected <T> T get(Class<T> serviceClass) {
return directory.get(serviceClass);
}
}
......@@ -73,4 +73,19 @@ public abstract class AbstractTableRow implements TableRow {
protected void add(String id, Object value) {
cells.put(id, value.toString());
}
/**
* Concatenates an arbitrary number of objects, using their
* toString() methods.
*
* @param items the items to concatenate
* @return a concatenated string
*/
protected static String concat(Object... items) {
StringBuilder sb = new StringBuilder();
for (Object o : items) {
sb.append(o);
}
return sb.toString();
}
}
......
......@@ -22,13 +22,15 @@ import org.onosproject.app.ApplicationService;
import org.onosproject.app.ApplicationState;
import org.onosproject.core.Application;
import org.onosproject.core.ApplicationId;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.table.AbstractTableRow;
import org.onosproject.ui.table.RowComparator;
import org.onosproject.ui.table.TableRow;
import org.onosproject.ui.table.TableUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
......@@ -37,40 +39,57 @@ import static org.onosproject.app.ApplicationState.ACTIVE;
/**
* Message handler for application view related messages.
*/
public class ApplicationViewMessageHandler extends UiMessageHandler {
public class ApplicationViewMessageHandler extends UiMessageHandlerTwo {
/**
* Creates a new message handler for the application messages.
*/
protected ApplicationViewMessageHandler() {
super(ImmutableSet.of("appDataRequest", "appManagementRequest"));
}
private static final String APP_DATA_REQ = "appDataRequest";
private static final String APP_MGMT_REQ = "appManagementRequest";
@Override
public void process(ObjectNode message) {
String type = eventType(message);
if (type.equals("appDataRequest")) {
sendAppList(message);
} else if (type.equals("appManagementRequest")) {
processManagementCommand(message);
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(
new AppDataRequest(),
new AppMgmtRequest()
);
}
// ======================================================================
private final class AppDataRequest extends RequestHandler {
private AppDataRequest() {
super(APP_DATA_REQ);
}
private void sendAppList(ObjectNode message) {
ObjectNode payload = payload(message);
@Override
public void process(long sid, ObjectNode payload) {
RowComparator rc = TableUtils.createRowComparator(payload);
ApplicationService service = get(ApplicationService.class);
TableRow[] rows = generateTableRows(service);
Arrays.sort(rows, rc);
ObjectNode rootNode = mapper.createObjectNode();
ObjectNode rootNode = MAPPER.createObjectNode();
rootNode.set("apps", TableUtils.generateArrayNode(rows));
connection().sendMessage("appDataResponse", 0, rootNode);
sendMessage("appDataResponse", 0, rootNode);
}
private void processManagementCommand(ObjectNode message) {
ObjectNode payload = payload(message);
private TableRow[] generateTableRows(ApplicationService service) {
List<TableRow> list = service.getApplications().stream()
.map(application -> new ApplicationTableRow(service, application))
.collect(Collectors.toList());
return list.toArray(new TableRow[list.size()]);
}
}
// ======================================================================
private final class AppMgmtRequest extends RequestHandler {
private AppMgmtRequest() {
super(APP_MGMT_REQ);
}
@Override
public void process(long sid, ObjectNode payload) {
String action = string(payload, "action");
String name = string(payload, "name");
if (action != null && name != null) {
......@@ -83,16 +102,11 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
} else if (action.equals("uninstall")) {
service.uninstall(appId);
}
sendAppList(message);
chain(APP_DATA_REQ, sid, payload);
}
}
private TableRow[] generateTableRows(ApplicationService service) {
List<TableRow> list = service.getApplications().stream()
.map(application -> new ApplicationTableRow(service, application))
.collect(Collectors.toList());
return list.toArray(new TableRow[list.size()]);
}
// ======================================================================
/**
* TableRow implementation for
......
......@@ -23,57 +23,61 @@ import org.joda.time.format.DateTimeFormat;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.table.AbstractTableRow;
import org.onosproject.ui.table.RowComparator;
import org.onosproject.ui.table.TableRow;
import org.onosproject.ui.table.TableUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* Message handler for cluster view related messages.
*/
public class ClusterViewMessageHandler extends UiMessageHandler {
public class ClusterViewMessageHandler extends UiMessageHandlerTwo {
/**
* Creates a new message handler for the cluster messages.
*/
protected ClusterViewMessageHandler() {
super(ImmutableSet.of("clusterDataRequest"));
}
private static final String CLUSTER_DATA_REQ = "clusterDataRequest";
@Override
public void process(ObjectNode message) {
String type = eventType(message);
if (type.equals("clusterDataRequest")) {
sendClusterList(message);
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(new ClusterDataRequest());
}
// ======================================================================
private final class ClusterDataRequest extends RequestHandler {
private ClusterDataRequest() {
super(CLUSTER_DATA_REQ);
}
private void sendClusterList(ObjectNode message) {
ObjectNode payload = payload(message);
@Override
public void process(long sid, ObjectNode payload) {
RowComparator rc = TableUtils.createRowComparator(payload);
ClusterService service = get(ClusterService.class);
TableRow[] rows = generateTableRows(service);
Arrays.sort(rows, rc);
ObjectNode rootNode = mapper.createObjectNode();
ObjectNode rootNode = MAPPER.createObjectNode();
rootNode.set("clusters", TableUtils.generateArrayNode(rows));
connection().sendMessage("clusterDataResponse", 0, rootNode);
sendMessage("clusterDataResponse", 0, rootNode);
}
private TableRow[] generateTableRows(ClusterService service) {
List<TableRow> list = new ArrayList<>();
for (ControllerNode node : service.getNodes()) {
list.add(new ControllerNodeTableRow(service, node));
}
List<TableRow> list = service.getNodes().stream()
.map(node -> new ControllerNodeTableRow(service, node))
.collect(Collectors.toList());
return list.toArray(new TableRow[list.size()]);
}
}
// ======================================================================
/**
* TableRow implementation for {@link ControllerNode controller nodes}.
......
......@@ -27,7 +27,8 @@ import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.link.LinkService;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.table.AbstractTableRow;
import org.onosproject.ui.table.RowComparator;
import org.onosproject.ui.table.TableRow;
......@@ -35,6 +36,7 @@ import org.onosproject.ui.table.TableUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
......@@ -42,7 +44,10 @@ import java.util.Set;
/**
* Message handler for device view related messages.
*/
public class DeviceViewMessageHandler extends UiMessageHandler {
public class DeviceViewMessageHandler extends UiMessageHandlerTwo {
private static final String DEV_DATA_REQ = "deviceDataRequest";
private static final String DEV_DETAIL_REQ = "deviceDetailRequest";
private static final String ID = "id";
private static final String TYPE = "type";
......@@ -65,46 +70,62 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
private static final String NAME = "name";
/**
* Creates a new message handler for the device messages.
*/
protected DeviceViewMessageHandler() {
super(ImmutableSet.of("deviceDataRequest", "deviceDetailsRequest"));
}
@Override
public void process(ObjectNode message) {
String type = eventType(message);
if (type.equals("deviceDataRequest")) {
dataRequest(message);
} else if (type.equals("deviceDetailsRequest")) {
detailsRequest(message);
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(
new DataRequestHandler(),
new DetailRequestHandler()
);
}
// ======================================================================
private final class DataRequestHandler extends RequestHandler {
private DataRequestHandler() {
super(DEV_DATA_REQ);
}
private void dataRequest(ObjectNode message) {
ObjectNode payload = payload(message);
@Override
public void process(long sid, ObjectNode payload) {
RowComparator rc = TableUtils.createRowComparator(payload);
DeviceService service = get(DeviceService.class);
MastershipService mastershipService = get(MastershipService.class);
TableRow[] rows = generateTableRows(service, mastershipService);
Arrays.sort(rows, rc);
ObjectNode rootNode = mapper.createObjectNode();
ObjectNode rootNode = MAPPER.createObjectNode();
rootNode.set("devices", TableUtils.generateArrayNode(rows));
connection().sendMessage("deviceDataResponse", 0, rootNode);
sendMessage("deviceDataResponse", 0, rootNode);
}
private void detailsRequest(ObjectNode message) {
ObjectNode payload = payload(message);
private TableRow[] generateTableRows(DeviceService service,
MastershipService mastershipService) {
List<TableRow> list = new ArrayList<>();
for (Device dev : service.getDevices()) {
list.add(new DeviceTableRow(service, mastershipService, dev));
}
return list.toArray(new TableRow[list.size()]);
}
}
// ======================================================================
private final class DetailRequestHandler extends RequestHandler {
private DetailRequestHandler() {
super(DEV_DETAIL_REQ);
}
@Override
public void process(long sid, ObjectNode payload) {
String id = string(payload, "id", "of:0000000000000000");
DeviceId deviceId = DeviceId.deviceId(id);
DeviceService service = get(DeviceService.class);
MastershipService ms = get(MastershipService.class);
Device device = service.getDevice(deviceId);
ObjectNode data = mapper.createObjectNode();
ObjectNode data = MAPPER.createObjectNode();
data.put(ID, deviceId.toString());
data.put(TYPE, device.type().toString());
......@@ -117,7 +138,7 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
data.put(MASTER_ID, ms.getMasterFor(deviceId).toString());
data.put(PROTOCOL, device.annotations().value(PROTOCOL));
ArrayNode ports = mapper.createArrayNode();
ArrayNode ports = MAPPER.createArrayNode();
List<Port> portList = new ArrayList<>(service.getPorts(deviceId));
Collections.sort(portList, (p1, p2) -> {
......@@ -130,22 +151,13 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
}
data.set(PORTS, ports);
ObjectNode rootNode = mapper.createObjectNode();
ObjectNode rootNode = MAPPER.createObjectNode();
rootNode.set("details", data);
connection().sendMessage("deviceDetailsResponse", 0, rootNode);
}
private TableRow[] generateTableRows(DeviceService service,
MastershipService mastershipService) {
List<TableRow> list = new ArrayList<>();
for (Device dev : service.getDevices()) {
list.add(new DeviceTableRow(service, mastershipService, dev));
}
return list.toArray(new TableRow[list.size()]);
sendMessage("deviceDetailsResponse", 0, rootNode);
}
private ObjectNode portData(Port p, DeviceId id) {
ObjectNode port = mapper.createObjectNode();
ObjectNode port = MAPPER.createObjectNode();
LinkService ls = get(LinkService.class);
String name = p.annotations().value(AnnotationKeys.PORT_NAME);
......@@ -169,6 +181,9 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
return port;
}
}
private static String getTypeIconId(Device d) {
return DEV_ICON_PREFIX + d.type().toString();
}
......
......@@ -26,7 +26,8 @@ import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.table.AbstractTableRow;
import org.onosproject.ui.table.RowComparator;
import org.onosproject.ui.table.TableRow;
......@@ -34,6 +35,7 @@ import org.onosproject.ui.table.TableUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
......@@ -41,44 +43,44 @@ import java.util.Set;
/**
* Message handler for flow view related messages.
*/
public class FlowViewMessageHandler extends UiMessageHandler {
public class FlowViewMessageHandler extends UiMessageHandlerTwo {
private static final String NO_DEV = "none";
private static final String FLOW_DATA_REQ = "flowDataRequest";
/**
* Creates a new message handler for the flow messages.
*/
protected FlowViewMessageHandler() {
super(ImmutableSet.of("flowDataRequest"));
}
private static final String NO_DEV = "none";
@Override
public void process(ObjectNode message) {
String type = eventType(message);
if (type.equals("flowDataRequest")) {
sendFlowList(message);
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(new FlowDataRequest());
}
// ======================================================================
private final class FlowDataRequest extends RequestHandler {
private FlowDataRequest() {
super(FLOW_DATA_REQ);
}
private void sendFlowList(ObjectNode message) {
ObjectNode payload = payload(message);
@Override
public void process(long sid, ObjectNode payload) {
RowComparator rc = TableUtils.createRowComparator(payload);
String uri = string(payload, "devId", NO_DEV);
ObjectNode rootNode;
if (uri.equals(NO_DEV)) {
rootNode = mapper.createObjectNode();
rootNode.set("flows", mapper.createArrayNode());
rootNode = MAPPER.createObjectNode();
rootNode.set("flows", MAPPER.createArrayNode());
} else {
DeviceId deviceId = DeviceId.deviceId(uri);
FlowRuleService service = get(FlowRuleService.class);
TableRow[] rows = generateTableRows(service, deviceId);
Arrays.sort(rows, rc);
rootNode = mapper.createObjectNode();
rootNode = MAPPER.createObjectNode();
rootNode.set("flows", TableUtils.generateArrayNode(rows));
}
connection().sendMessage("flowDataResponse", 0, rootNode);
sendMessage("flowDataResponse", 0, rootNode);
}
private TableRow[] generateTableRows(FlowRuleService service,
......@@ -89,6 +91,9 @@ public class FlowViewMessageHandler extends UiMessageHandler {
}
return list.toArray(new TableRow[list.size()]);
}
}
// ======================================================================
/**
* TableRow implementation for {@link org.onosproject.net.flow.FlowRule flows}.
......
......@@ -21,7 +21,8 @@ import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.Host;
import org.onosproject.net.HostLocation;
import org.onosproject.net.host.HostService;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.table.AbstractTableRow;
import org.onosproject.ui.table.RowComparator;
import org.onosproject.ui.table.TableRow;
......@@ -29,6 +30,7 @@ import org.onosproject.ui.table.TableUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static com.google.common.base.Strings.isNullOrEmpty;
......@@ -36,34 +38,35 @@ import static com.google.common.base.Strings.isNullOrEmpty;
/**
* Message handler for host view related messages.
*/
public class HostViewMessageHandler extends UiMessageHandler {
public class HostViewMessageHandler extends UiMessageHandlerTwo {
private static final String HOST_DATA_REQ = "hostDataRequest";
/**
* Creates a new message handler for the host messages.
*/
protected HostViewMessageHandler() {
super(ImmutableSet.of("hostDataRequest"));
}
@Override
public void process(ObjectNode message) {
String type = eventType(message);
if (type.equals("hostDataRequest")) {
sendHostList(message);
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(new HostDataRequest());
}
// ======================================================================
private final class HostDataRequest extends RequestHandler {
private HostDataRequest() {
super(HOST_DATA_REQ);
}
private void sendHostList(ObjectNode message) {
ObjectNode payload = payload(message);
@Override
public void process(long sid, ObjectNode payload) {
RowComparator rc = TableUtils.createRowComparator(payload);
HostService service = get(HostService.class);
TableRow[] rows = generateTableRows(service);
Arrays.sort(rows, rc);
ObjectNode rootNode = mapper.createObjectNode();
ObjectNode rootNode = MAPPER.createObjectNode();
rootNode.set("hosts", TableUtils.generateArrayNode(rows));
connection().sendMessage("hostDataResponse", 0, rootNode);
sendMessage("hostDataResponse", 0, rootNode);
}
private TableRow[] generateTableRows(HostService service) {
......@@ -73,6 +76,9 @@ public class HostViewMessageHandler extends UiMessageHandler {
}
return list.toArray(new TableRow[list.size()]);
}
}
// ======================================================================
/**
* TableRow implementation for {@link Host hosts}.
......
......@@ -31,7 +31,8 @@ import org.onosproject.net.intent.MultiPointToSinglePointIntent;
import org.onosproject.net.intent.PathIntent;
import org.onosproject.net.intent.PointToPointIntent;
import org.onosproject.net.intent.SinglePointToMultiPointIntent;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.table.AbstractTableRow;
import org.onosproject.ui.table.RowComparator;
import org.onosproject.ui.table.TableRow;
......@@ -39,40 +40,42 @@ import org.onosproject.ui.table.TableUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Message handler for intent view related messages.
*/
public class IntentViewMessageHandler extends UiMessageHandler {
public class IntentViewMessageHandler extends UiMessageHandlerTwo {
private static final String INTENT_DATA_REQ = "intentDataRequest";
/**
* Creates a new message handler for the intent messages.
*/
protected IntentViewMessageHandler() {
super(ImmutableSet.of("intentDataRequest"));
}
@Override
public void process(ObjectNode message) {
String type = eventType(message);
if (type.equals("intentDataRequest")) {
sendIntentList(message);
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(new IntentDataRequest());
}
// ======================================================================
private final class IntentDataRequest extends RequestHandler {
private IntentDataRequest() {
super(INTENT_DATA_REQ);
}
private void sendIntentList(ObjectNode message) {
ObjectNode payload = payload(message);
@Override
public void process(long sid, ObjectNode payload) {
RowComparator rc = TableUtils.createRowComparator(payload);
IntentService service = get(IntentService.class);
TableRow[] rows = generateTableRows(service);
Arrays.sort(rows, rc);
ObjectNode rootNode = mapper.createObjectNode();
ObjectNode rootNode = MAPPER.createObjectNode();
rootNode.set("intents", TableUtils.generateArrayNode(rows));
connection().sendMessage("intentDataResponse", 0, rootNode);
sendMessage("intentDataResponse", 0, rootNode);
}
private TableRow[] generateTableRows(IntentService service) {
......@@ -82,6 +85,9 @@ public class IntentViewMessageHandler extends UiMessageHandler {
}
return list.toArray(new TableRow[list.size()]);
}
}
// ======================================================================
/**
* TableRow implementation for {@link Intent intents}.
......
......@@ -23,7 +23,8 @@ import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import org.onosproject.net.link.LinkService;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.onosproject.ui.impl.TopologyViewMessageHandlerBase.BiLink;
import org.onosproject.ui.table.AbstractTableRow;
import org.onosproject.ui.table.RowComparator;
......@@ -32,6 +33,7 @@ import org.onosproject.ui.table.TableUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
......@@ -40,34 +42,35 @@ import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.addLink;
/**
* Message handler for link view related messages.
*/
public class LinkViewMessageHandler extends UiMessageHandler {
public class LinkViewMessageHandler extends UiMessageHandlerTwo {
private static final String LINK_DATA_REQ = "linkDataRequest";
/**
* Creates a new message handler for the link messages.
*/
protected LinkViewMessageHandler() {
super(ImmutableSet.of("linkDataRequest"));
}
@Override
public void process(ObjectNode message) {
String type = eventType(message);
if (type.equals("linkDataRequest")) {
sendLinkList(message);
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(new LinkDataRequest());
}
// ======================================================================
private final class LinkDataRequest extends RequestHandler {
private LinkDataRequest() {
super(LINK_DATA_REQ);
}
private void sendLinkList(ObjectNode message) {
ObjectNode payload = payload(message);
@Override
public void process(long sid, ObjectNode payload) {
RowComparator rc = TableUtils.createRowComparator(payload, "one");
LinkService service = get(LinkService.class);
TableRow[] rows = generateTableRows(service);
Arrays.sort(rows, rc);
ObjectNode rootNode = mapper.createObjectNode();
ObjectNode rootNode = MAPPER.createObjectNode();
rootNode.set("links", TableUtils.generateArrayNode(rows));
connection().sendMessage("linkDataResponse", 0, rootNode);
sendMessage("linkDataResponse", 0, rootNode);
}
private TableRow[] generateTableRows(LinkService service) {
......@@ -81,6 +84,9 @@ public class LinkViewMessageHandler extends UiMessageHandler {
biLinks.values().forEach(biLink -> list.add(new LinkTableRow(biLink)));
return list.toArray(new TableRow[list.size()]);
}
}
// ======================================================================
/**
* TableRow implementation for {@link org.onosproject.net.Link links}.
......
......@@ -54,9 +54,12 @@ import org.onosproject.net.intent.IntentListener;
import org.onosproject.net.intent.MultiPointToSinglePointIntent;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkListener;
import org.onosproject.ui.JsonUtils;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
......@@ -79,6 +82,26 @@ import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
*/
public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
private static final String REQ_DETAILS = "requestDetails";
private static final String UPDATE_META = "updateMeta";
private static final String ADD_HOST_INTENT = "addHostIntent";
private static final String ADD_MULTI_SRC_INTENT = "addMultiSourceIntent";
private static final String REQ_RELATED_INTENTS = "requestRelatedIntents";
private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
private static final String REQ_ALL_TRAFFIC = "requestAllTraffic";
private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
private static final String CANCEL_TRAFFIC = "cancelTraffic";
private static final String REQ_SUMMARY = "requestSummary";
private static final String CANCEL_SUMMARY = "cancelSummary";
private static final String EQ_MASTERS = "equalizeMasters";
private static final String SPRITE_LIST_REQ = "spriteListRequest";
private static final String SPRITE_DATA_REQ = "spriteDataRequest";
private static final String TOPO_START = "topoStart";
private static final String TOPO_STOP = "topoStop";
private static final String APP_ID = "org.onosproject.gui";
private static final long TRAFFIC_FREQUENCY = 5000;
......@@ -111,11 +134,11 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
private final Accumulator<Event> eventAccummulator = new InternalEventAccummulator();
private TimerTask trafficTask;
private ObjectNode trafficEvent;
private TimerTask trafficTask = null;
private TrafficEvent trafficEvent = null;
private TimerTask summaryTask;
private ObjectNode summaryEvent;
private TimerTask summaryTask = null;
private boolean summaryRunning = false;
private boolean listenersRemoved = false;
......@@ -127,30 +150,6 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
private List<Intent> selectedIntents;
private int currentIntentIndex = -1;
/**
* Creates a new web-socket for serving data to GUI topology view.
*/
public TopologyViewMessageHandler() {
super(ImmutableSet.of("topoStart",
"topoStop",
"requestDetails",
"updateMeta",
"addHostIntent",
"addMultiSourceIntent",
"requestRelatedIntents",
"requestNextRelatedIntent",
"requestPrevRelatedIntent",
"requestSelectedIntentTraffic",
"requestAllTraffic",
"requestDeviceLinkFlows",
"cancelTraffic",
"requestSummary",
"cancelSummary",
"equalizeMasters",
"spriteListRequest",
"spriteDataRequest"
));
}
@Override
public void init(UiConnection connection, ServiceDirectory directory) {
......@@ -167,172 +166,184 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
super.destroy();
}
// Processes the specified event.
@Override
public void process(ObjectNode event) {
String type = string(event, "event", "unknown");
if (type.equals("requestDetails")) {
requestDetails(event);
} else if (type.equals("updateMeta")) {
updateMetaUi(event);
} else if (type.equals("addHostIntent")) {
createHostIntent(event);
} else if (type.equals("addMultiSourceIntent")) {
createMultiSourceIntent(event);
} else if (type.equals("requestRelatedIntents")) {
stopTrafficMonitoring();
requestRelatedIntents(event);
} else if (type.equals("requestNextRelatedIntent")) {
stopTrafficMonitoring();
requestAnotherRelatedIntent(event, +1);
} else if (type.equals("requestPrevRelatedIntent")) {
stopTrafficMonitoring();
requestAnotherRelatedIntent(event, -1);
} else if (type.equals("requestSelectedIntentTraffic")) {
requestSelectedIntentTraffic(event);
startTrafficMonitoring(event);
} else if (type.equals("requestAllTraffic")) {
requestAllTraffic(event);
startTrafficMonitoring(event);
} else if (type.equals("requestDeviceLinkFlows")) {
requestDeviceLinkFlows(event);
startTrafficMonitoring(event);
} else if (type.equals("cancelTraffic")) {
cancelTraffic(event);
} else if (type.equals("requestSummary")) {
requestSummary(event);
startSummaryMonitoring(event);
} else if (type.equals("cancelSummary")) {
stopSummaryMonitoring();
} else if (type.equals("equalizeMasters")) {
equalizeMasters(event);
} else if (type.equals("spriteListRequest")) {
sendSpriteList(event);
} else if (type.equals("spriteDataRequest")) {
sendSpriteData(event);
} else if (type.equals("topoStart")) {
sendAllInitialData();
} else if (type.equals("topoStop")) {
cancelAllRequests();
}
protected Collection<RequestHandler> getHandlers() {
return ImmutableSet.of(
new TopoStart(),
new TopoStop(),
new ReqSummary(),
new CancelSummary(),
new SpriteListReq(),
new SpriteDataReq(),
new RequestDetails(),
new UpdateMeta(),
new EqMasters(),
// TODO: migrate traffic related to separate app
new AddHostIntent(),
new AddMultiSourceIntent(),
new ReqRelatedIntents(),
new ReqNextIntent(),
new ReqPrevIntent(),
new ReqSelectedIntentTraffic(),
new ReqAllTraffic(),
new ReqDevLinkFlows(),
new CancelTraffic()
);
}
// ==================================================================
private final class TopoStart extends RequestHandler {
private TopoStart() {
super(TOPO_START);
}
// Sends the specified data to the client.
protected synchronized void sendMessage(ObjectNode data) {
UiConnection connection = connection();
if (connection != null) {
connection.sendMessage(data);
}
}
private void sendAllInitialData() {
@Override
public void process(long sid, ObjectNode payload) {
addListeners();
sendAllInstances(null);
sendAllDevices();
sendAllLinks();
sendAllHosts();
}
}
private final class TopoStop extends RequestHandler {
private TopoStop() {
super(TOPO_STOP);
}
private void cancelAllRequests() {
@Override
public void process(long sid, ObjectNode payload) {
stopSummaryMonitoring();
stopTrafficMonitoring();
}
// Sends all controller nodes to the client as node-added messages.
private void sendAllInstances(String messageType) {
List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
Collections.sort(nodes, NODE_COMPARATOR);
for (ControllerNode node : nodes) {
sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
messageType));
}
private final class ReqSummary extends RequestHandler {
private ReqSummary() {
super(REQ_SUMMARY);
}
// Sends all devices to the client as device-added messages.
private void sendAllDevices() {
// Send optical first, others later for layered rendering
for (Device device : deviceService.getDevices()) {
if (device.type() == Device.Type.ROADM) {
sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
@Override
public void process(long sid, ObjectNode payload) {
requestSummary(sid);
startSummaryMonitoring();
}
}
for (Device device : deviceService.getDevices()) {
if (device.type() != Device.Type.ROADM) {
sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
private final class CancelSummary extends RequestHandler {
private CancelSummary() {
super(CANCEL_SUMMARY);
}
@Override
public void process(long sid, ObjectNode payload) {
stopSummaryMonitoring();
}
}
// Sends all links to the client as link-added messages.
private void sendAllLinks() {
// Send optical first, others later for layered rendering
for (Link link : linkService.getLinks()) {
if (link.type() == Link.Type.OPTICAL) {
sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
private final class SpriteListReq extends RequestHandler {
private SpriteListReq() {
super(SPRITE_LIST_REQ);
}
}
for (Link link : linkService.getLinks()) {
if (link.type() != Link.Type.OPTICAL) {
sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
@Override
public void process(long sid, ObjectNode payload) {
ObjectNode root = mapper.createObjectNode();
ArrayNode names = mapper.createArrayNode();
get(SpriteService.class).getNames().forEach(names::add);
root.set("names", names);
sendMessage("spriteListResponse", sid, root);
}
}
private final class SpriteDataReq extends RequestHandler {
private SpriteDataReq() {
super(SPRITE_DATA_REQ);
}
// Sends all hosts to the client as host-added messages.
private void sendAllHosts() {
for (Host host : hostService.getHosts()) {
sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
@Override
public void process(long sid, ObjectNode payload) {
String name = string(payload, "name");
ObjectNode root = mapper.createObjectNode();
root.set("data", get(SpriteService.class).get(name));
sendMessage("spriteDataResponse", sid, root);
}
}
// Sends back device or host details.
private void requestDetails(ObjectNode event) {
ObjectNode payload = payload(event);
private final class RequestDetails extends RequestHandler {
private RequestDetails() {
super(REQ_DETAILS);
}
@Override
public void process(long sid, ObjectNode payload) {
String type = string(payload, "class", "unknown");
long sid = number(event, "sid");
String id = JsonUtils.string(payload, "id");
if (type.equals("device")) {
sendMessage(deviceDetails(deviceId(string(payload, "id")), sid));
sendMessage(deviceDetails(deviceId(id), sid));
} else if (type.equals("host")) {
sendMessage(hostDetails(hostId(string(payload, "id")), sid));
sendMessage(hostDetails(hostId(id), sid));
}
}
}
private final class UpdateMeta extends RequestHandler {
private UpdateMeta() {
super(UPDATE_META);
}
@Override
public void process(long sid, ObjectNode payload) {
updateMetaUi(payload);
}
}
private final class EqMasters extends RequestHandler {
private EqMasters() {
super(EQ_MASTERS);
}
@Override
public void process(long sid, ObjectNode payload) {
directory.get(MastershipAdminService.class).balanceRoles();
}
}
// === TODO: move traffic related classes to traffic app
// Creates host-to-host intent.
private void createHostIntent(ObjectNode event) {
ObjectNode payload = payload(event);
long id = number(event, "sid");
private final class AddHostIntent extends RequestHandler {
private AddHostIntent() {
super(ADD_HOST_INTENT);
}
@Override
public void process(long sid, ObjectNode payload) {
// TODO: add protection against device ids and non-existent hosts.
HostId one = hostId(string(payload, "one"));
HostId two = hostId(string(payload, "two"));
HostToHostIntent intent =
HostToHostIntent.builder()
HostToHostIntent intent = HostToHostIntent.builder()
.appId(appId)
.one(one)
.two(two)
.build();
intentService.submit(intent);
startMonitoringIntent(event, intent);
startMonitoringIntent(intent);
}
}
private final class AddMultiSourceIntent extends RequestHandler {
private AddMultiSourceIntent() {
super(ADD_MULTI_SRC_INTENT);
}
// Creates multi-source-to-single-dest intent.
private void createMultiSourceIntent(ObjectNode event) {
ObjectNode payload = payload(event);
long id = number(event, "sid");
@Override
public void process(long sid, ObjectNode payload) {
// TODO: add protection against device ids and non-existent hosts.
Set<HostId> src = getHostIds((ArrayNode) payload.path("src"));
HostId dst = hostId(string(payload, "dst"));
......@@ -355,18 +366,201 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
.build();
intentService.submit(intent);
startMonitoringIntent(event, intent);
startMonitoringIntent(intent);
}
}
private final class ReqRelatedIntents extends RequestHandler {
private ReqRelatedIntents() {
super(REQ_RELATED_INTENTS);
}
@Override
public void process(long sid, ObjectNode payload) {
// Cancel any other traffic monitoring mode.
stopTrafficMonitoring();
if (!payload.has("ids")) {
return;
}
// Get the set of selected hosts and their intents.
ArrayNode ids = (ArrayNode) payload.path("ids");
selectedHosts = getHosts(ids);
selectedDevices = getDevices(ids);
selectedIntents = intentFilter.findPathIntents(
selectedHosts, selectedDevices, intentService.getIntents());
currentIntentIndex = -1;
if (haveSelectedIntents()) {
// Send a message to highlight all links of all monitored intents.
sendMessage(trafficMessage(new TrafficClass("primary", selectedIntents)));
}
// TODO: Re-introduce once the client click vs hover gesture stuff is sorted out.
// String hover = string(payload, "hover");
// if (!isNullOrEmpty(hover)) {
// // If there is a hover node, include it in the selection and find intents.
// processHoverExtendedSelection(sid, hover);
// }
}
}
private final class ReqNextIntent extends RequestHandler {
private ReqNextIntent() {
super(REQ_NEXT_INTENT);
}
@Override
public void process(long sid, ObjectNode payload) {
stopTrafficMonitoring();
requestAnotherRelatedIntent(+1);
}
}
private final class ReqPrevIntent extends RequestHandler {
private ReqPrevIntent() {
super(REQ_PREV_INTENT);
}
@Override
public void process(long sid, ObjectNode payload) {
stopTrafficMonitoring();
requestAnotherRelatedIntent(-1);
}
}
private final class ReqSelectedIntentTraffic extends RequestHandler {
private ReqSelectedIntentTraffic() {
super(REQ_SEL_INTENT_TRAFFIC);
}
@Override
public void process(long sid, ObjectNode payload) {
trafficEvent =
new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload);
requestSelectedIntentTraffic();
startTrafficMonitoring();
}
}
private final class ReqAllTraffic extends RequestHandler {
private ReqAllTraffic() {
super(REQ_ALL_TRAFFIC);
}
@Override
public void process(long sid, ObjectNode payload) {
trafficEvent =
new TrafficEvent(TrafficEvent.Type.ALL_TRAFFIC, payload);
requestAllTraffic();
}
}
private final class ReqDevLinkFlows extends RequestHandler {
private ReqDevLinkFlows() {
super(REQ_DEV_LINK_FLOWS);
}
@Override
public void process(long sid, ObjectNode payload) {
trafficEvent =
new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload);
requestDeviceLinkFlows(payload);
}
}
private final class CancelTraffic extends RequestHandler {
private CancelTraffic() {
super(CANCEL_TRAFFIC);
}
@Override
public void process(long sid, ObjectNode payload) {
selectedIntents = null;
sendMessage(trafficMessage());
stopTrafficMonitoring();
}
}
//=======================================================================
// Sends the specified data to the client.
protected synchronized void sendMessage(ObjectNode data) {
UiConnection connection = connection();
if (connection != null) {
connection.sendMessage(data);
}
}
// Subscribes for summary messages.
private synchronized void requestSummary(long sid) {
sendMessage(summmaryMessage(sid));
}
private synchronized void startMonitoringIntent(ObjectNode event, Intent intent) {
private void cancelAllRequests() {
stopSummaryMonitoring();
stopTrafficMonitoring();
}
// Sends all controller nodes to the client as node-added messages.
private void sendAllInstances(String messageType) {
List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
Collections.sort(nodes, NODE_COMPARATOR);
for (ControllerNode node : nodes) {
sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node),
messageType));
}
}
// Sends all devices to the client as device-added messages.
private void sendAllDevices() {
// Send optical first, others later for layered rendering
for (Device device : deviceService.getDevices()) {
if (device.type() == Device.Type.ROADM) {
sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
}
}
for (Device device : deviceService.getDevices()) {
if (device.type() != Device.Type.ROADM) {
sendMessage(deviceMessage(new DeviceEvent(DEVICE_ADDED, device)));
}
}
}
// Sends all links to the client as link-added messages.
private void sendAllLinks() {
// Send optical first, others later for layered rendering
for (Link link : linkService.getLinks()) {
if (link.type() == Link.Type.OPTICAL) {
sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
}
}
for (Link link : linkService.getLinks()) {
if (link.type() != Link.Type.OPTICAL) {
sendMessage(linkMessage(new LinkEvent(LINK_ADDED, link)));
}
}
}
// Sends all hosts to the client as host-added messages.
private void sendAllHosts() {
for (Host host : hostService.getHosts()) {
sendMessage(hostMessage(new HostEvent(HOST_ADDED, host)));
}
}
private synchronized void startMonitoringIntent(Intent intent) {
selectedHosts = new HashSet<>();
selectedDevices = new HashSet<>();
selectedIntents = new ArrayList<>();
selectedIntents.add(intent);
currentIntentIndex = -1;
requestAnotherRelatedIntent(event, +1);
requestSelectedIntentTraffic(event);
requestAnotherRelatedIntent(+1);
requestSelectedIntentTraffic();
}
......@@ -392,31 +586,27 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
}
private synchronized long startTrafficMonitoring(ObjectNode event) {
private synchronized void startTrafficMonitoring() {
stopTrafficMonitoring();
trafficEvent = event;
trafficTask = new TrafficMonitor();
timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY);
return number(event, "sid");
}
private synchronized void stopTrafficMonitoring() {
if (trafficTask != null) {
trafficTask.cancel();
trafficTask = null;
trafficEvent = null;
}
}
// Subscribes for host traffic messages.
private synchronized void requestAllTraffic(ObjectNode event) {
long sid = startTrafficMonitoring(event);
sendMessage(trafficSummaryMessage(sid));
private synchronized void requestAllTraffic() {
startTrafficMonitoring();
sendMessage(trafficSummaryMessage());
}
private void requestDeviceLinkFlows(ObjectNode event) {
ObjectNode payload = payload(event);
long sid = startTrafficMonitoring(event);
private void requestDeviceLinkFlows(ObjectNode payload) {
startTrafficMonitoring();
// Get the set of selected hosts and their intents.
ArrayNode ids = (ArrayNode) payload.path("ids");
......@@ -424,46 +614,13 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
Set<Device> devices = getDevices(ids);
// If there is a hover node, include it in the hosts and find intents.
String hover = string(payload, "hover");
String hover = JsonUtils.string(payload, "hover");
if (!isNullOrEmpty(hover)) {
addHover(hosts, devices, hover);
}
sendMessage(flowSummaryMessage(sid, devices));
}
// Requests related intents message.
private synchronized void requestRelatedIntents(ObjectNode event) {
ObjectNode payload = payload(event);
if (!payload.has("ids")) {
return;
}
long sid = number(event, "sid");
// Cancel any other traffic monitoring mode.
stopTrafficMonitoring();
// Get the set of selected hosts and their intents.
ArrayNode ids = (ArrayNode) payload.path("ids");
selectedHosts = getHosts(ids);
selectedDevices = getDevices(ids);
selectedIntents = intentFilter.findPathIntents(selectedHosts, selectedDevices,
intentService.getIntents());
currentIntentIndex = -1;
if (haveSelectedIntents()) {
// Send a message to highlight all links of all monitored intents.
sendMessage(trafficMessage(sid, new TrafficClass("primary", selectedIntents)));
sendMessage(flowSummaryMessage(devices));
}
// FIXME: Re-introduce one the client click vs hover gesture stuff is sorted out.
// String hover = string(payload, "hover");
// if (!isNullOrEmpty(hover)) {
// // If there is a hover node, include it in the selection and find intents.
// processHoverExtendedSelection(sid, hover);
// }
}
private boolean haveSelectedIntents() {
return selectedIntents != null && !selectedIntents.isEmpty();
......@@ -483,12 +640,12 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
secondary.removeAll(primary);
// Send a message to highlight all links of all monitored intents.
sendMessage(trafficMessage(sid, new TrafficClass("primary", primary),
sendMessage(trafficMessage(new TrafficClass("primary", primary),
new TrafficClass("secondary", secondary)));
}
// Requests next or previous related intent.
private void requestAnotherRelatedIntent(ObjectNode event, int offset) {
private void requestAnotherRelatedIntent(int offset) {
if (haveSelectedIntents()) {
currentIntentIndex = currentIntentIndex + offset;
if (currentIntentIndex < 0) {
......@@ -496,13 +653,13 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
} else if (currentIntentIndex >= selectedIntents.size()) {
currentIntentIndex = 0;
}
sendSelectedIntent(event);
sendSelectedIntent();
}
}
// Sends traffic information on the related intents with the currently
// selected intent highlighted.
private void sendSelectedIntent(ObjectNode event) {
private void sendSelectedIntent() {
Intent selectedIntent = selectedIntents.get(currentIntentIndex);
log.info("Requested next intent {}", selectedIntent.id());
......@@ -513,13 +670,12 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
secondary.remove(selectedIntent);
// Send a message to highlight all links of the selected intent.
sendMessage(trafficMessage(number(event, "sid"),
new TrafficClass("primary", primary),
sendMessage(trafficMessage(new TrafficClass("primary", primary),
new TrafficClass("secondary", secondary)));
}
// Requests monitoring of traffic for the selected intent.
private void requestSelectedIntentTraffic(ObjectNode event) {
private void requestSelectedIntentTraffic() {
if (haveSelectedIntents()) {
if (currentIntentIndex < 0) {
currentIntentIndex = 0;
......@@ -531,61 +687,23 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
primary.add(selectedIntent);
// Send a message to highlight all links of the selected intent.
sendMessage(trafficMessage(number(event, "sid"),
new TrafficClass("primary", primary, true)));
}
sendMessage(trafficMessage(new TrafficClass("primary", primary, true)));
}
// Cancels sending traffic messages.
private void cancelTraffic(ObjectNode event) {
selectedIntents = null;
sendMessage(trafficMessage(number(event, "sid")));
stopTrafficMonitoring();
}
private synchronized long startSummaryMonitoring(ObjectNode event) {
private synchronized void startSummaryMonitoring() {
stopSummaryMonitoring();
summaryEvent = event;
summaryTask = new SummaryMonitor();
timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY);
return number(event, "sid");
summaryRunning = true;
}
private synchronized void stopSummaryMonitoring() {
if (summaryEvent != null) {
if (summaryTask != null) {
summaryTask.cancel();
summaryTask = null;
summaryEvent = null;
}
}
// Subscribes for summary messages.
private synchronized void requestSummary(ObjectNode event) {
sendMessage(summmaryMessage(number(event, "sid")));
}
// Forces mastership role rebalancing.
private void equalizeMasters(ObjectNode event) {
directory.get(MastershipAdminService.class).balanceRoles();
}
// Sends a list of sprite names.
private void sendSpriteList(ObjectNode event) {
ObjectNode root = mapper.createObjectNode();
ArrayNode names = mapper.createArrayNode();
get(SpriteService.class).getNames().forEach(names::add);
root.set("names", names);
sendMessage(envelope("spriteListResponse", number(event, "sid"), root));
}
// Sends requested sprite data.
private void sendSpriteData(ObjectNode event) {
String name = event.path("payload").path("name").asText();
ObjectNode root = mapper.createObjectNode();
root.set("data", get(SpriteService.class).get(name));
sendMessage(envelope("spriteDataResponse", number(event, "sid"), root));
summaryRunning = false;
}
......@@ -666,8 +784,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
private class InternalIntentListener implements IntentListener {
@Override
public void event(IntentEvent event) {
if (trafficEvent != null) {
requestSelectedIntentTraffic(trafficEvent);
if (trafficTask != null) {
requestSelectedIntentTraffic();
}
eventAccummulator.add(event);
}
......@@ -681,19 +799,38 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
}
}
// encapsulate
private static class TrafficEvent {
enum Type { ALL_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT }
private final Type type;
private final ObjectNode payload;
TrafficEvent(Type type, ObjectNode payload) {
this.type = type;
this.payload = payload;
}
}
// Periodic update of the traffic information
private class TrafficMonitor extends TimerTask {
@Override
public void run() {
try {
if (trafficEvent != null) {
String type = string(trafficEvent, "event", "unknown");
if (type.equals("requestAllTraffic")) {
requestAllTraffic(trafficEvent);
} else if (type.equals("requestDeviceLinkFlows")) {
requestDeviceLinkFlows(trafficEvent);
} else if (type.equals("requestSelectedIntentTraffic")) {
requestSelectedIntentTraffic(trafficEvent);
switch (trafficEvent.type) {
case ALL_TRAFFIC:
requestAllTraffic();
break;
case DEV_LINK_FLOWS:
requestDeviceLinkFlows(trafficEvent.payload);
break;
case SEL_INTENT:
requestSelectedIntentTraffic();
break;
default:
// nothing to do
break;
}
}
} catch (Exception e) {
......@@ -708,8 +845,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
@Override
public void run() {
try {
if (summaryEvent != null) {
requestSummary(summaryEvent);
if (summaryRunning) {
requestSummary(0);
}
} catch (Exception e) {
log.warn("Unable to handle summary request due to {}", e.getMessage());
......@@ -727,8 +864,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
@Override
public void processItems(List<Event> items) {
try {
if (summaryEvent != null) {
sendMessage(summmaryMessage(0));
if (summaryRunning) {
requestSummary(0);
}
} catch (Exception e) {
log.warn("Unable to handle summary request due to {}", e.getMessage());
......@@ -737,4 +874,3 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
}
}
}
......
......@@ -63,8 +63,9 @@ import org.onosproject.net.statistic.Load;
import org.onosproject.net.statistic.StatisticService;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.ui.JsonUtils;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -100,11 +101,13 @@ import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
/**
* Facility for creating messages bound for the topology viewer.
*/
public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
public abstract class TopologyViewMessageHandlerBase extends UiMessageHandlerTwo {
protected static final Logger log = LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
protected static final Logger log =
LoggerFactory.getLogger(TopologyViewMessageHandlerBase.class);
private static final ProviderId PID = new ProviderId("core", "org.onosproject.core", true);
private static final ProviderId PID =
new ProviderId("core", "org.onosproject.core", true);
private static final String COMPACT = "%s/%s-%s/%s";
private static final double KB = 1024;
......@@ -133,15 +136,6 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
/**
* Creates a new message handler for the specified set of message types.
*
* @param messageTypes set of message types
*/
protected TopologyViewMessageHandlerBase(Set<String> messageTypes) {
super(messageTypes);
}
/**
* Returns read-only view of the meta-ui information.
*
* @return map of id to meta-ui mementos
......@@ -168,26 +162,6 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
}
// Retrieves the payload from the specified event.
protected ObjectNode payload(ObjectNode event) {
return (ObjectNode) event.path("payload");
}
// Returns the specified node property as a number
protected long number(ObjectNode node, String name) {
return node.path(name).asLong();
}
// Returns the specified node property as a string.
protected String string(ObjectNode node, String name) {
return node.path(name).asText();
}
// Returns the specified node property as a string.
protected String string(ObjectNode node, String name, String defaultValue) {
return node.path(name).asText(defaultValue);
}
// Returns the specified set of IP addresses as a string.
private String ip(Set<IpAddress> ipAddresses) {
Iterator<IpAddress> it = ipAddresses.iterator();
......@@ -222,21 +196,11 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces a log message event bound to the client.
private ObjectNode message(String severity, long id, String message) {
return envelope("message", id,
mapper.createObjectNode()
ObjectNode payload = mapper.createObjectNode()
.put("severity", severity)
.put("message", message));
}
.put("message", message);
// Puts the payload into an envelope and returns it.
protected ObjectNode envelope(String type, long sid, ObjectNode payload) {
ObjectNode event = mapper.createObjectNode();
event.put("event", type);
if (sid > 0) {
event.put("sid", sid);
}
event.set("payload", payload);
return event;
return JsonUtils.envelope("message", id, payload);
}
// Produces a set of all hosts listed in the specified JSON array.
......@@ -320,7 +284,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
((event.type() == INSTANCE_ADDED) ? "addInstance" :
((event.type() == INSTANCE_REMOVED ? "removeInstance" :
"addInstance")));
return envelope(type, 0, payload);
return JsonUtils.envelope(type, 0, payload);
}
// Produces a device event message to the client.
......@@ -347,7 +311,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
((event.type() == DEVICE_REMOVED) ? "removeDevice" : "updateDevice");
return envelope(type, 0, payload);
return JsonUtils.envelope(type, 0, payload);
}
// Produces a link event message to the client.
......@@ -364,7 +328,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
.put("dstPort", link.dst().port().toString());
String type = (event.type() == LINK_ADDED) ? "addLink" :
((event.type() == LINK_REMOVED) ? "removeLink" : "updateLink");
return envelope(type, 0, payload);
return JsonUtils.envelope(type, 0, payload);
}
// Produces a host event message to the client.
......@@ -385,7 +349,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
String type = (event.type() == HOST_ADDED) ? "addHost" :
((event.type() == HOST_REMOVED) ? "removeHost" : "updateHost");
return envelope(type, 0, payload);
return JsonUtils.envelope(type, 0, payload);
}
// Encodes the specified host location into a JSON object.
......@@ -447,15 +411,15 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
}
// Updates meta UI information for the specified object.
protected void updateMetaUi(ObjectNode event) {
ObjectNode payload = payload(event);
metaUi.put(string(payload, "id"), (ObjectNode) payload.path("memento"));
protected void updateMetaUi(ObjectNode payload) {
metaUi.put(JsonUtils.string(payload, "id"),
JsonUtils.node(payload, "memento"));
}
// Returns summary response.
protected ObjectNode summmaryMessage(long sid) {
Topology topology = topologyService.currentTopology();
return envelope("showSummary", sid,
return JsonUtils.envelope("showSummary", sid,
json("ONOS Summary", "node",
new Prop("Devices", format(topology.deviceCount())),
new Prop("Links", format(topology.linkCount())),
......@@ -474,7 +438,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
String name = annot.value(AnnotationKeys.NAME);
int portCount = deviceService.getPorts(deviceId).size();
int flowCount = getFlowCount(deviceId);
return envelope("showDetails", sid,
return JsonUtils.envelope("showDetails", sid,
json(isNullOrEmpty(name) ? deviceId.toString() : name,
device.type().toString().toLowerCase(),
new Prop("URI", deviceId.toString()),
......@@ -552,7 +516,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
String type = annot.value(AnnotationKeys.TYPE);
String name = annot.value(AnnotationKeys.NAME);
String vlan = host.vlan().toString();
return envelope("showDetails", sid,
return JsonUtils.envelope("showDetails", sid,
json(isNullOrEmpty(name) ? hostId.toString() : name,
isNullOrEmpty(type) ? "endstation" : type,
new Prop("MAC", host.mac().toString()),
......@@ -565,7 +529,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces JSON message to trigger traffic overview visualization
protected ObjectNode trafficSummaryMessage(long sid) {
protected ObjectNode trafficSummaryMessage() {
ObjectNode payload = mapper.createObjectNode();
ArrayNode paths = mapper.createArrayNode();
payload.set("paths", paths);
......@@ -603,7 +567,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
}
}
}
return envelope("showTraffic", sid, payload);
return JsonUtils.envelope("showTraffic", 0, payload);
}
private Collection<BiLink> consolidateLinks(Iterable<Link> links) {
......@@ -615,7 +579,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
}
// Produces JSON message to trigger flow overview visualization
protected ObjectNode flowSummaryMessage(long sid, Set<Device> devices) {
protected ObjectNode flowSummaryMessage(Set<Device> devices) {
ObjectNode payload = mapper.createObjectNode();
ArrayNode paths = mapper.createArrayNode();
payload.set("paths", paths);
......@@ -626,7 +590,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
addLinkFlows(link, paths, counts.get(link));
}
}
return envelope("showTraffic", sid, payload);
return JsonUtils.envelope("showTraffic", 0, payload);
}
private void addLinkFlows(Link link, ArrayNode paths, Integer count) {
......@@ -644,7 +608,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces JSON message to trigger traffic visualization
protected ObjectNode trafficMessage(long sid, TrafficClass... trafficClasses) {
protected ObjectNode trafficMessage(TrafficClass... trafficClasses) {
ObjectNode payload = mapper.createObjectNode();
ArrayNode paths = mapper.createArrayNode();
payload.set("paths", paths);
......@@ -670,7 +634,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : "");
}
return envelope("showTraffic", sid, payload);
return JsonUtils.envelope("showTraffic", 0, payload);
}
// Classifies the link traffic according to the specified classes.
......
......@@ -24,8 +24,8 @@ import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiMessageHandlerTwo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -56,7 +56,7 @@ public class UiWebSocket
private long lastActive = System.currentTimeMillis();
private Map<String, UiMessageHandler> handlers;
private Map<String, UiMessageHandlerTwo> handlers;
/**
* Creates a new web-socket for serving data to GUI.
......@@ -123,7 +123,7 @@ public class UiWebSocket
try {
ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
String type = message.path("event").asText("unknown");
UiMessageHandler handler = handlers.get(type);
UiMessageHandlerTwo handler = handlers.get(type);
if (handler != null) {
handler.process(message);
} else {
......