Added a set of abstractions for flow rule subsystem.
Added an apps source subtree.
diff --git a/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java b/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
new file mode 100644
index 0000000..996a81f
--- /dev/null
+++ b/apps/tvue/src/main/java/org/onlab/onos/tvue/TopologyResource.java
@@ -0,0 +1,153 @@
+package org.onlab.onos.tvue;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.ElementId;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.link.LinkService;
+import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyGraph;
+import org.onlab.onos.net.topology.TopologyService;
+import org.onlab.onos.net.topology.TopologyVertex;
+import org.onlab.rest.BaseResource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Topology viewer resource.
+ */
+@Path("topology")
+public class TopologyResource extends BaseResource {
+
+ @Path("/graph")
+ @GET
+ @Produces("application/json")
+ public Response graph() {
+ ObjectMapper mapper = new ObjectMapper();
+
+ // Fetch the services we'll be using.
+ DeviceService deviceService = get(DeviceService.class);
+ HostService hostService = get(HostService.class);
+ TopologyService topologyService = get(TopologyService.class);
+
+ // Fetch the current topology and its graph that we'll use to render.
+ Topology topo = topologyService.currentTopology();
+ TopologyGraph graph = topologyService.getGraph(topo);
+
+ // Build all interior vertexes, i.e. no end-station hosts yet
+ ArrayNode vertexesNode = mapper.createArrayNode();
+ for (TopologyVertex vertex : graph.getVertexes()) {
+ vertexesNode.add(json(mapper, vertex.deviceId(), 2,
+ deviceService.isAvailable(vertex.deviceId())));
+ }
+
+ // Now scan all links and count number of them between the same devices
+ // using a normalized link key.
+ Map<String, AggLink> linkRecords = aggregateLinks();
+
+ // Now build all interior edges using the aggregated links.
+ ArrayNode edgesNode = mapper.createArrayNode();
+ for (AggLink lr : linkRecords.values()) {
+ edgesNode.add(json(mapper, lr.links.size(), lr.link.src(), lr.link.dst()));
+ }
+
+ // Merge the exterior and interior vertexes and inject host links as
+ // the exterior edges.
+// Iterator<Host> hosts = hostService.getHosts();
+// while (hosts.hasNext()) {
+// Host host = hosts.next();
+// vertexesNode.add(json(mapper, host.id().ip().toString(), 3, true));
+// edgesNode.add(json(mapper, 1, host.ip().toString(),
+// host.location().elementId().uri()));
+// }
+
+ // Now put the vertexes and edges into a root node and ship them off
+ ObjectNode rootNode = mapper.createObjectNode();
+ rootNode.put("vertexes", vertexesNode);
+ rootNode.put("edges", edgesNode);
+ return Response.ok(rootNode.toString()).build();
+ }
+
+ // Scan all links and counts number of them between the same devices
+ // using a normalized link key.
+ private Map<String, AggLink> aggregateLinks() {
+ Map<String, AggLink> aggLinks = new HashMap<>();
+ LinkService linkService = get(LinkService.class);
+ for (Link link : linkService.getLinks()) {
+ String key = key(link);
+ AggLink lr = aggLinks.get(key);
+ if (lr == null) {
+ lr = new AggLink(key);
+ aggLinks.put(key, lr);
+ }
+ lr.addLink(link);
+ }
+ return aggLinks;
+ }
+
+ // Produces JSON for a graph vertex.
+ private ObjectNode json(ObjectMapper mapper, ElementId id, int group,
+ boolean isOnline) {
+ return mapper.createObjectNode()
+ .put("name", id.uri().toString())
+ .put("group", group)
+ .put("online", isOnline);
+ }
+
+ // Produces JSON for a graph edge.
+ private ObjectNode json(ObjectMapper mapper, int count,
+ ConnectPoint src, ConnectPoint dst) {
+ return json(mapper, count, id(src), id(dst));
+ }
+
+ // Produces JSON for a graph edge.
+ private ObjectNode json(ObjectMapper mapper, int count, String src, String dst) {
+ return mapper.createObjectNode()
+ .put("source", src).put("target", dst).put("value", count);
+ }
+
+ // Aggregate link of all links between the same devices regardless of
+ // their direction.
+ private class AggLink {
+ Link link; // representative links
+
+ final String key;
+ final Set<Link> links = new HashSet<>();
+
+ AggLink(String key) {
+ this.key = key;
+ }
+
+ void addLink(Link link) {
+ links.add(link);
+ if (this.link == null) {
+ this.link = link;
+ }
+ }
+ }
+
+ // Returns a canonical key for the specified link.
+ static String key(Link link) {
+ String s = id(link.src());
+ String d = id(link.dst());
+ return s.compareTo(d) > 0 ? d + s : s + d;
+ }
+
+ // Returns a formatted string for the element associated with the given
+ // connection point.
+ private static String id(ConnectPoint cp) {
+ return cp.elementId().uri().toString();
+ }
+
+}
diff --git a/apps/tvue/src/main/webapp/WEB-INF/web.xml b/apps/tvue/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d958283
--- /dev/null
+++ b/apps/tvue/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ id="ONOS" version="2.5">
+ <display-name>ONOS GUI</display-name>
+
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ </welcome-file-list>
+
+ <servlet>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.packages</param-name>
+ <param-value>org.onlab.onos.gui</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>JAX-RS Service</servlet-name>
+ <url-pattern>/rs/*</url-pattern>
+ </servlet-mapping>
+
+</web-app>
\ No newline at end of file
diff --git a/apps/tvue/src/main/webapp/index.html b/apps/tvue/src/main/webapp/index.html
new file mode 100644
index 0000000..f959f93
--- /dev/null
+++ b/apps/tvue/src/main/webapp/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>ONOS GUI</title>
+</head>
+<body>
+ <h1>ONOS GUI</h1>
+ Sort of...
+</body>
+</html>
\ No newline at end of file