Check-point before refactoring the topology provider stuff.
diff --git a/cli/src/main/java/org/onlab/onos/cli/GreetCommand.java b/cli/src/main/java/org/onlab/onos/cli/GreetCommand.java
deleted file mode 100644
index 71e56f1..0000000
--- a/cli/src/main/java/org/onlab/onos/cli/GreetCommand.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.onlab.onos.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.onlab.onos.GreetService;
-
-/**
- * Simple command example to demonstrate use of Karaf shell extensions; shows
- * use of an optional parameter as well.
- */
-@Command(scope = "onos", name = "greet", description = "Issues a greeting")
-public class GreetCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "name", description = "Name to greet",
-              required = false, multiValued = false)
-    String name = "dude";
-
-    @Override
-    protected Object doExecute() throws Exception {
-        print(getService(GreetService.class).yo(name));
-        return null;
-    }
-}
diff --git a/cli/src/main/java/org/onlab/onos/cli/NameCompleter.java b/cli/src/main/java/org/onlab/onos/cli/NameCompleter.java
deleted file mode 100644
index bcf0707..0000000
--- a/cli/src/main/java/org/onlab/onos/cli/NameCompleter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.onlab.onos.cli;
-
-import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.completer.StringsCompleter;
-import org.onlab.onos.GreetService;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.SortedSet;
-
-/**
- * Simple example of a command-line parameter completer.
- * For a more open-ended sets a more efficient implementation would be required.
- */
-public class NameCompleter implements Completer {
-    @Override
-    public int complete(String buffer, int cursor, List<String> candidates) {
-        // Delegate string completer
-        StringsCompleter delegate = new StringsCompleter();
-
-        // Fetch our service and feed it's offerings to the string completer
-        GreetService greetService = AbstractShellCommand.get(GreetService.class);
-        Iterator<String> it = greetService.names().iterator();
-        SortedSet<String> strings = delegate.getStrings();
-        while (it.hasNext()) {
-            strings.add(it.next());
-        }
-
-        // Now let the completer do the work for figuring out what to offer.
-        return delegate.complete(buffer, cursor, candidates);
-    }
-
-}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 96c494d..fb44199 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -30,18 +30,9 @@
                 <ref component-id="deviceIdCompleter"/>
             </completers>
         </command>
-
-        <command>
-            <action class="org.onlab.onos.cli.GreetCommand"/>
-            <completers>
-                <ref component-id="nameCompleter"/>
-            </completers>
-        </command>
     </command-bundle>
 
     <bean id="deviceIdCompleter" class="org.onlab.onos.cli.net.DeviceIdCompleter"/>
     <bean id="roleCompleter" class="org.onlab.onos.cli.net.RoleCompleter"/>
 
-    <bean id="nameCompleter" class="org.onlab.onos.cli.NameCompleter"/>
-
 </blueprint>
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyDescription.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyDescription.java
index dd0102d..bcb199d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyDescription.java
@@ -1,12 +1,14 @@
 package org.onlab.onos.net.topology;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
 import org.onlab.graph.Graph;
-import org.onlab.graph.GraphPathSearch;
 import org.onlab.onos.net.Description;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Link;
 
-import java.util.Set;
+import static org.onlab.graph.GraphPathSearch.Result;
 
 /**
  * Describes attribute(s) of a network topology.
@@ -22,50 +24,39 @@
     long timestamp();
 
     /**
-     * Returns the topology graph.
+     * Returns the topology graph in immutable form.
      *
      * @return network graph
      */
     Graph<TopoVertex, TopoEdge> graph();
 
     /**
-     * Returns the results of the path search through the network graph. This
-     * is assumed to contain results of seach fro the given device to all
-     * other devices.
+     * Returns an immutable map of path search results for each source device.
      *
-     * @param srcDeviceId source device identifier
-     * @return path search result for the given source node
+     * @return map of path search result for each source node
      */
-    GraphPathSearch.Result pathResults(DeviceId srcDeviceId);
+    ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> pathsBySource();
 
     /**
      * Returns the set of topology SCC clusters.
      *
      * @return set of SCC clusters
      */
-    Set<TopologyCluster> clusters();
+    ImmutableSet<TopologyCluster> clusters();
 
     /**
-     * Returns the set of devices contained by the specified topology cluster.
+     * Returns an immutable set multi-map of devices for each cluster.
      *
-     * @return set of devices that belong to the specified cluster
+     * @return set multi-map of devices that belong to each cluster
      */
-    Set<DeviceId> clusterDevices(TopologyCluster cluster);
+    ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster();
 
     /**
-     * Returns the set of infrastructure links contained by the specified cluster.
+     * Returns an immutable set multi-map of links for each cluster.
      *
-     * @return set of links that form the given cluster
+     * @return set multi-map of links that belong to each cluster
      */
-    Set<Link> clusterLinks(TopologyCluster cluster);
-
-    /**
-     * Returns the topology SCC cluster which contains the given device.
-     *
-     * @param deviceId device identifier
-     * @return topology cluster that contains the specified device
-     */
-    TopologyCluster clusterFor(DeviceId deviceId);
+    ImmutableSetMultimap<TopologyCluster, Link> linksByCluster();
 
 }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
index 7c6be33..149804b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/TopologyService.java
@@ -21,6 +21,7 @@
 
     /**
      * Indicates whether the specified topology is the latest or not.
+     *
      * @param topology topology descriptor
      * @return true if the topology is the most recent; false otherwise
      */
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
index c20acd8..3417ca1 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/DefaultTopology.java
@@ -1,8 +1,23 @@
 package org.onlab.onos.net.trivial.topology.impl;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import org.onlab.graph.Graph;
+import org.onlab.graph.GraphPathSearch;
 import org.onlab.onos.net.AbstractModel;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
 import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.topology.TopoEdge;
+import org.onlab.onos.net.topology.TopoVertex;
 import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyCluster;
+import org.onlab.onos.net.topology.TopologyDescription;
+
+import java.util.Set;
 
 /**
  * Default implementation of the topology descriptor. This carries the
@@ -11,29 +26,33 @@
 public class DefaultTopology extends AbstractModel implements Topology {
 
     private final long time;
-    private final int clusterCount;
-    private final int deviceCount;
-    private final int linkCount;
     private final int pathCount;
 
+    private final Graph<TopoVertex, TopoEdge> graph;
+    private final ImmutableMap<DeviceId, GraphPathSearch.Result<TopoVertex, TopoEdge>> results;
+
+    private final ImmutableSet<TopologyCluster> clusters;
+    private final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
+    private final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
+    private final ImmutableSet connectPoints;
+
     /**
      * Creates a topology descriptor attributed to the specified provider.
      *
-     * @param providerId   identity of the provider
-     * @param time         creation time in system nanos
-     * @param clusterCount number of clusters
-     * @param deviceCount  number of devices
-     * @param linkCount    number of links
-     * @param pathCount    number of pre-computed paths
+     * @param providerId  identity of the provider
+     * @param description data describing the new topology
      */
-    DefaultTopology(ProviderId providerId, long time, int clusterCount,
-                    int deviceCount, int linkCount, int pathCount) {
+    DefaultTopology(ProviderId providerId, TopologyDescription description) {
         super(providerId);
-        this.time = time;
-        this.clusterCount = clusterCount;
-        this.deviceCount = deviceCount;
-        this.linkCount = linkCount;
-        this.pathCount = pathCount;
+        this.time = description.timestamp();
+        this.graph = description.graph();
+        this.results = description.pathsBySource();
+        this.clusters = description.clusters();
+        this.devicesByCluster = description.devicesByCluster();
+        this.linksByCluster = description.linksByCluster();
+
+        this.connectPoints = ImmutableSet.of();
+        this.pathCount = 0;
     }
 
     @Override
@@ -43,17 +62,17 @@
 
     @Override
     public int clusterCount() {
-        return clusterCount;
+        return clusters.size();
     }
 
     @Override
     public int deviceCount() {
-        return deviceCount;
+        return graph.getVertexes().size();
     }
 
     @Override
     public int linkCount() {
-        return linkCount;
+        return graph.getEdges().size();
     }
 
     @Override
@@ -61,4 +80,23 @@
         return pathCount;
     }
 
+    Set<TopologyCluster> getClusters() {
+        return clusters;
+    }
+
+    Graph<TopoVertex, TopoEdge> getGraph() {
+        return graph;
+    }
+
+    boolean isInfrastructure(ConnectPoint connectPoint) {
+        return connectPoints.contains(connectPoint);
+    }
+
+    boolean isInBroadcastTree(ConnectPoint connectPoint) {
+        return false;
+    }
+
+    Set<Path> getPaths(DeviceId src, DeviceId dst) {
+        return null; // pointToPointPaths.get(key(src, dst));
+    }
 }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java
index 70f001c..e6e54b8 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyManager.java
@@ -79,19 +79,28 @@
     @Override
     public boolean isLatest(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.isLatest(topology);
+        return store.isLatest(defaultTopology(topology));
+    }
+
+    // Validates the specified topology and returns it as a default
+    private DefaultTopology defaultTopology(Topology topology) {
+        if (topology instanceof DefaultTopology) {
+            return (DefaultTopology) topology;
+        }
+        throw new IllegalArgumentException("Topology class " + topology.getClass() +
+                                                   " not supported");
     }
 
     @Override
     public Set<TopologyCluster> getClusters(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.getClusters(topology);
+        return store.getClusters(defaultTopology(topology));
     }
 
     @Override
     public Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
         checkNotNull(topology, TOPOLOGY_NULL);
-        return store.getGraph(topology);
+        return store.getGraph(defaultTopology(topology));
     }
 
     @Override
@@ -99,7 +108,7 @@
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(src, DEVICE_ID_NULL);
         checkNotNull(dst, DEVICE_ID_NULL);
-        return store.getPaths(topology, src, dst);
+        return store.getPaths(defaultTopology(topology), src, dst);
     }
 
     @Override
@@ -108,21 +117,21 @@
         checkNotNull(src, DEVICE_ID_NULL);
         checkNotNull(dst, DEVICE_ID_NULL);
         checkNotNull(weight, "Link weight cannot be null");
-        return store.getPaths(topology, src, dst, weight);
+        return store.getPaths(defaultTopology(topology), src, dst, weight);
     }
 
     @Override
     public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(connectPoint, CONNECTION_POINT_NULL);
-        return store.isInfrastructure(topology, connectPoint);
+        return store.isInfrastructure(defaultTopology(topology), connectPoint);
     }
 
     @Override
     public boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) {
         checkNotNull(topology, TOPOLOGY_NULL);
         checkNotNull(connectPoint, CONNECTION_POINT_NULL);
-        return store.isInBroadcastTree(topology, connectPoint);
+        return store.isInBroadcastTree(defaultTopology(topology), connectPoint);
     }
 
     @Override
@@ -156,7 +165,8 @@
 
             log.info("Topology changed due to: {}", // to be removed soon
                      reasons == null ? "initial compute" : reasons);
-            TopologyEvent event = store.updateTopology(topoDescription, reasons);
+            TopologyEvent event = store.updateTopology(provider().id(),
+                                                       topoDescription, reasons);
             if (event != null) {
                 log.info("Topology changed due to: {}",
                          reasons == null ? "initial compute" : reasons);
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java
index 1b9766d..57cb3a8 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/impl/SimpleTopologyStore.java
@@ -5,6 +5,7 @@
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Path;
+import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.onos.net.topology.LinkWeight;
 import org.onlab.onos.net.topology.TopoEdge;
 import org.onlab.onos.net.topology.TopoVertex;
@@ -50,8 +51,8 @@
      * @param topology topology descriptor
      * @return set of clusters
      */
-    Set<TopologyCluster> getClusters(Topology topology) {
-        return null;
+    Set<TopologyCluster> getClusters(DefaultTopology topology) {
+        return topology.getClusters();
     }
 
     /**
@@ -60,8 +61,8 @@
      * @param topology topology descriptor
      * @return graph view
      */
-    Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
-        return null;
+    Graph<TopoVertex, TopoEdge> getGraph(DefaultTopology topology) {
+        return topology.getGraph();
     }
 
     /**
@@ -72,8 +73,8 @@
      * @param dst      destination device
      * @return set of shortest paths
      */
-    Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
-        return null;
+    Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst) {
+        return topology.getPaths(src, dst);
     }
 
     /**
@@ -85,7 +86,7 @@
      * @param weight   link weight function
      * @return set of shortest paths
      */
-    Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
+    Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst,
                        LinkWeight weight) {
         return null;
     }
@@ -97,8 +98,8 @@
      * @param connectPoint connection point
      * @return true if infrastructure; false otherwise
      */
-    boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
-        return false;
+    boolean isInfrastructure(DefaultTopology topology, ConnectPoint connectPoint) {
+        return topology.isInfrastructure(connectPoint);
     }
 
     /**
@@ -108,20 +109,34 @@
      * @param connectPoint connection point
      * @return true if in broadcast tree; false otherwise
      */
-    boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) {
-        return false;
+    boolean isInBroadcastTree(DefaultTopology topology, ConnectPoint connectPoint) {
+        return topology.isInBroadcastTree(connectPoint);
     }
 
     /**
      * Generates a new topology snapshot from the specified description.
      *
+     * @param providerId      provider identification
      * @param topoDescription topology description
      * @param reasons         list of events that triggered the update
      * @return topology update event or null if the description is old
      */
-    TopologyEvent updateTopology(TopologyDescription topoDescription,
+    TopologyEvent updateTopology(ProviderId providerId,
+                                 TopologyDescription topoDescription,
                                  List<Event> reasons) {
-        return null;
+        // First off, make sure that what we're given is indeed newer than
+        // what we already have.
+        if (current != null && topoDescription.timestamp() < current.time()) {
+            return null;
+        }
+
+        // Have the default topology construct self from the description data.
+        DefaultTopology newTopology =
+                new DefaultTopology(providerId, topoDescription);
+
+        // Promote the new topology to current and return a ready-to-send event.
+        current = newTopology;
+        return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current);
     }
 
 }
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopologyDescription.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopologyDescription.java
index 408e6af..37411a2 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopologyDescription.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/DefaultTopologyDescription.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.net.trivial.topology.provider.impl;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.Maps;
@@ -26,6 +27,7 @@
 import java.util.Set;
 
 import static com.google.common.collect.ImmutableSetMultimap.Builder;
+import static com.google.common.collect.ImmutableSetMultimap.builder;
 import static org.onlab.graph.GraphPathSearch.Result;
 import static org.onlab.graph.TarjanGraphSearch.SCCResult;
 import static org.onlab.onos.net.Link.Type.INDIRECT;
@@ -43,13 +45,12 @@
     private final long nanos;
     private final Map<DeviceId, TopoVertex> vertexesById = Maps.newHashMap();
     private final Graph<TopoVertex, TopoEdge> graph;
-    private final Map<DeviceId, Result<TopoVertex, TopoEdge>> results;
-    private final Map<ClusterId, TopologyCluster> clusters;
+    private final ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> results;
 
-    // Secondary look-up indexes
-    private ImmutableSetMultimap<ClusterId, DeviceId> devicesByCluster;
-    private ImmutableSetMultimap<ClusterId, Link> linksByCluster;
-    private Map<DeviceId, TopologyCluster> clustersByDevice = Maps.newHashMap();
+    // Cluster-related structures
+    private final ImmutableSet<TopologyCluster> clusters;
+    private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
+    private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
 
     /**
      * Creates a topology description to carry topology vitals to the core.
@@ -76,31 +77,25 @@
     }
 
     @Override
-    public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) {
-        return results.get(srcDeviceId);
+    public ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> pathsBySource() {
+        return results;
     }
 
     @Override
-    public Set<TopologyCluster> clusters() {
-        return ImmutableSet.copyOf(clusters.values());
+    public ImmutableSet<TopologyCluster> clusters() {
+        return clusters;
     }
 
     @Override
-    public Set<DeviceId> clusterDevices(TopologyCluster cluster) {
-        return devicesByCluster.get(cluster.id());
+    public ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
+        return devicesByCluster;
     }
 
     @Override
-    public Set<Link> clusterLinks(TopologyCluster cluster) {
-        return linksByCluster.get(cluster.id());
+    public ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
+        return linksByCluster;
     }
 
-    @Override
-    public TopologyCluster clusterFor(DeviceId deviceId) {
-        return clustersByDevice.get(deviceId);
-    }
-
-
     // Link weight for measuring link cost as hop count with indirect links
     // being as expensive as traversing the entire graph to assume the worst.
     private static class HopCountLinkWeight implements LinkWeight {
@@ -156,29 +151,31 @@
 
     // Computes the default shortest paths for all source/dest pairs using
     // the multi-path Dijkstra and hop-count as path cost.
-    private Map<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() {
+    private ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() {
         LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
-        Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap();
+        ImmutableMap.Builder<DeviceId, Result<TopoVertex, TopoEdge>> results =
+                ImmutableMap.builder();
 
         // Search graph paths for each source to all destinations.
         for (TopoVertex src : vertexesById.values()) {
             results.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
         }
-        return results;
+        return results.build();
     }
 
     // Computes topology SCC clusters using Tarjan algorithm.
-    private Map<ClusterId, TopologyCluster> computeClusters() {
-        Map<ClusterId, TopologyCluster> clusters = Maps.newHashMap();
-        SCCResult<TopoVertex, TopoEdge> result = TARJAN.search(graph, new NoIndirectLinksWeight());
+    private ImmutableSet<TopologyCluster> computeClusters() {
+        ImmutableSet.Builder<TopologyCluster> clusterBuilder = ImmutableSet.builder();
+        SCCResult<TopoVertex, TopoEdge> result =
+                TARJAN.search(graph, new NoIndirectLinksWeight());
 
         // Extract both vertexes and edges from the results; the lists form
         // pairs along the same index.
         List<Set<TopoVertex>> clusterVertexes = result.clusterVertexes();
         List<Set<TopoEdge>> clusterEdges = result.clusterEdges();
 
-        Builder<ClusterId, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
-        Builder<ClusterId, Link> linksBuilder = ImmutableSetMultimap.builder();
+        Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
+        Builder<TopologyCluster, Link> linksBuilder = ImmutableSetMultimap.builder();
 
         // Scan over the lists and create a cluster from the results.
         for (int i = 0, n = result.clusterCount(); i < n; i++) {
@@ -189,10 +186,11 @@
                     new DefaultTopologyCluster(ClusterId.clusterId(i),
                                                vertexSet.size(), edgeSet.size(),
                                                findRoot(vertexSet).deviceId());
+            clusterBuilder.add(cluster);
             findClusterDevices(vertexSet, cluster, devicesBuilder);
             findClusterLinks(edgeSet, cluster, linksBuilder);
         }
-        return clusters;
+        return clusterBuilder.build();
     }
 
     // Scans through the set of cluster vertexes and puts their devices in a
@@ -200,11 +198,10 @@
     // the cluster.
     private void findClusterDevices(Set<TopoVertex> vertexSet,
                                     DefaultTopologyCluster cluster,
-                                    Builder<ClusterId, DeviceId> builder) {
+                                    Builder<TopologyCluster, DeviceId> builder) {
         for (TopoVertex vertex : vertexSet) {
             DeviceId deviceId = vertex.deviceId();
-            builder.put(cluster.id(), deviceId);
-            clustersByDevice.put(deviceId, cluster);
+            builder.put(cluster, deviceId);
         }
     }
 
@@ -212,9 +209,9 @@
     // multi-map associated with the cluster.
     private void findClusterLinks(Set<TopoEdge> edgeSet,
                                   DefaultTopologyCluster cluster,
-                                  Builder<ClusterId, Link> builder) {
+                                  Builder<TopologyCluster, Link> builder) {
         for (TopoEdge edge : edgeSet) {
-            builder.put(cluster.id(), edge.link());
+            builder.put(cluster, edge.link());
         }
     }
 
diff --git a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/SimpleTopologyProvider.java b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/SimpleTopologyProvider.java
index 777a0e4..5955c32 100644
--- a/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/SimpleTopologyProvider.java
+++ b/core/trivial/src/main/java/org/onlab/onos/net/trivial/topology/provider/impl/SimpleTopologyProvider.java
@@ -90,6 +90,8 @@
 
     @Deactivate
     public synchronized void deactivate() {
+        isStarted = false;
+
         deviceService.removeListener(deviceListener);
         linkService.removeListener(linkListener);
         providerRegistry.unregister(this);
@@ -98,7 +100,6 @@
         executor.shutdownNow();
         executor = null;
 
-        isStarted = false;
         log.info("Stopped");
     }
 
@@ -108,7 +109,7 @@
      *
      * @param reasons events which triggered the topology change
      */
-    private void triggerTopologyBuild(List<Event> reasons) {
+    private synchronized void triggerTopologyBuild(List<Event> reasons) {
         executor.execute(new TopologyBuilderTask(reasons));
     }