Added a set of abstractions for flow rule subsystem.
Added an apps source subtree.
diff --git a/apps/pom.xml b/apps/pom.xml
new file mode 100644
index 0000000..bd155f3
--- /dev/null
+++ b/apps/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onlab.onos</groupId>
+        <artifactId>onos</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-apps</artifactId>
+    <packaging>pom</packaging>
+
+    <description>ONOS sample applications</description>
+
+    <modules>
+        <module>tvue</module>
+    </modules>
+
+    <properties>
+        <web.context>default</web.context>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onlab.onos</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onlab.onos</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onlab.onos</groupId>
+            <artifactId>onlab-rest</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <version>1.18.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-grizzly2</artifactId>
+            <version>1.18.1</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <_wab>src/main/webapp/</_wab>
+                        <Bundle-SymbolicName>
+                            ${project.groupId}.${project.artifactId}
+                        </Bundle-SymbolicName>
+                        <Import-Package>
+                            org.osgi.framework,
+                            javax.ws.rs,javax.ws.rs.core,
+                            com.sun.jersey.api.core,
+                            com.sun.jersey.spi.container.servlet,
+                            com.sun.jersey.server.impl.container.servlet,
+                            com.fasterxml.jackson.databind,
+                            com.fasterxml.jackson.databind.node,
+                            org.onlab.rest.*,
+                            org.onlab.onos.*
+                        </Import-Package>
+                        <Web-ContextPath>${web.context}</Web-ContextPath>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/apps/tvue/pom.xml b/apps/tvue/pom.xml
new file mode 100644
index 0000000..d0380a3
--- /dev/null
+++ b/apps/tvue/pom.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onlab.onos</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-tvue</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS simple topology viewer</description>
+
+    <properties>
+        <web.context>/onos/tvue</web.context>
+    </properties>
+
+</project>
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