[ONOS-5919] (vCore) separate Intent store

Change-Id: I3b5b67d7286dafdf11bf3d8d4acafb59a3034e22
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntentStore.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntentStore.java
new file mode 100644
index 0000000..1c0d3cc
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkIntentStore.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.Key;
+
+import java.util.List;
+
+public interface VirtualNetworkIntentStore
+        extends VirtualStore<IntentEvent, IntentStoreDelegate> {
+
+    /**
+     * Returns the number of intents in the store.
+     *
+     * @param networkId the virtual network identifier
+     * @return the number of intents in the store
+     */
+    long getIntentCount(NetworkId networkId);
+
+    /**
+     * Returns an iterable of all intents in the store.
+     *
+     * @param networkId the virtual network identifier
+     * @return iterable  of all intents
+     */
+    Iterable<Intent> getIntents(NetworkId networkId);
+
+    /**
+     * Returns an iterable of all intent data objects in the store.
+     *
+     * @param networkId the virtual network identifier
+     * @param localOnly should only intents for which this instance is master
+     *                  be returned
+     * @param olderThan specified duration in milliseconds (0 for "now")
+     * @return iterable of all intent data objects
+     */
+    Iterable<IntentData> getIntentData(NetworkId networkId, boolean localOnly,
+                                       long olderThan);
+
+    /**
+     * Returns the state of the specified intent.
+     *
+     * @param networkId the virtual network identifier
+     * @param intentKey intent identification
+     * @return current intent state
+     */
+    IntentState getIntentState(NetworkId networkId, Key intentKey);
+
+    /**
+     * Returns the list of the installable events associated with the specified
+     * original intent.
+     *
+     * @param networkId the virtual network identifier
+     * @param intentKey original intent identifier
+     * @return compiled installable intents, or null if no installables exist
+     */
+    List<Intent> getInstallableIntents(NetworkId networkId, Key intentKey);
+
+    /**
+     * Writes an IntentData object to the store.
+     *
+     * @param networkId the virtual network identifier
+     * @param newData new intent data to write
+     */
+    void write(NetworkId networkId, IntentData newData);
+
+    /**
+     * Writes a batch of IntentData objects to the store. A batch has no
+     * semantics, this is simply a convenience API.
+     *
+     * @param networkId the virtual network identifier
+     * @param updates collection of intent data objects to write
+     */
+    void batchWrite(NetworkId networkId, Iterable<IntentData> updates);
+
+    /**
+     * Returns the intent with the specified identifier.
+     *
+     * @param networkId the virtual network identifier
+     * @param key key
+     * @return intent or null if not found
+     */
+    Intent getIntent(NetworkId networkId, Key key);
+
+    /**
+     * Returns the intent data object associated with the specified key.
+     *
+     * @param networkId the virtual network identifier
+     * @param key key to look up
+     * @return intent data object
+     */
+    IntentData getIntentData(NetworkId networkId, Key key);
+
+    /**
+     * Adds a new operation, which should be persisted and delegated.
+     *
+     * @param networkId the virtual network identifier
+     * @param intent operation
+     */
+    void addPending(NetworkId networkId, IntentData intent);
+
+    /**
+     * Checks to see whether the calling instance is the master for processing
+     * this intent, or more specifically, the key contained in this intent.
+     *
+     * @param networkId the virtual network identifier
+     * @param intentKey intentKey to check
+     * @return true if master; false, otherwise
+     */
+    //TODO better name
+    boolean isMaster(NetworkId networkId, Key intentKey);
+
+    /**
+     * Returns the intent requests pending processing.
+     *
+     * @param networkId the virtual network identifier
+     * @return pending intents
+     */
+    Iterable<Intent> getPending(NetworkId networkId);
+
+    /**
+     * Returns the intent data objects that are pending processing.
+     *
+     * @param networkId the virtual network identifier
+     * @return pending intent data objects
+     */
+    Iterable<IntentData> getPendingData(NetworkId networkId);
+
+    /**
+     * Returns the intent data object that are pending processing for a specfied
+     * key.
+     *
+     * @param networkId the virtual network identifier
+     * @param intentKey key to look up
+     * @return pending intent data object
+     */
+    IntentData getPendingData(NetworkId networkId, Key intentKey);
+
+    /**
+     * Returns the intent data objects that are pending processing for longer
+     * than the specified duration.
+     *
+     * @param networkId the virtual network identifier
+     * @param localOnly  should only intents for which this instance is master
+     *                   be returned
+     * @param olderThan specified duration in milliseconds (0 for "now")
+     * @return pending intent data objects
+     */
+    Iterable<IntentData> getPendingData(NetworkId networkId, boolean localOnly, long olderThan);
+
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
index 516d901..0e6b2a9 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
@@ -26,9 +26,6 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentData;
-import org.onosproject.net.intent.IntentState;
-import org.onosproject.net.intent.Key;
 import org.onosproject.store.Store;
 
 import java.util.Set;
@@ -238,22 +235,6 @@
     Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId);
 
     /**
-     * Add or update the intent to the store.
-     *
-     * @param intent virtual intent
-     * @param state  intent state
-     */
-    void addOrUpdateIntent(Intent intent, IntentState state);
-
-    /**
-     * Remove the virtual intent from the store.
-     *
-     * @param intentKey intent key
-     * @return intent data
-     */
-    IntentData removeIntent(Key intentKey);
-
-    /**
      * Adds the intent to tunnel identifier mapping to the store.
      *
      * @param intent   intent
@@ -276,34 +257,4 @@
      * @param tunnelId tunnel identifier
      */
     void removeTunnelId(Intent intent, TunnelId tunnelId);
-
-    /**
-     * Return all intents.
-     *
-     * @return set of intents
-     */
-    Set<Intent> getIntents();
-
-    /**
-     * Return the intent for the specified intent key.
-     *
-     * @param key intent key
-     * @return intent
-     */
-    Intent getIntent(Key key);
-
-    /**
-     * Return the set of intent data.
-     *
-     * @return set of intent data
-     */
-    Set<IntentData> getIntentData();
-
-    /**
-     * Return the intent data matching the intent key.
-     *
-     * @param key intent key
-     * @return intent data
-     */
-    IntentData getIntentData(Key key);
 }
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
index 41cd952..48ad983 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Iterators;
 import org.onosproject.incubator.net.virtual.NetworkId;
 import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
 import org.onosproject.incubator.net.virtual.VirtualNetworkService;
 import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
 import org.onosproject.incubator.net.virtual.VirtualPort;
@@ -64,6 +65,7 @@
 
     protected IntentService intentService;
     protected VirtualNetworkStore store;
+    protected VirtualNetworkIntentStore intentStore;
     protected WorkPartitionService partitionService;
 
     /**
@@ -78,6 +80,7 @@
         super(virtualNetworkManager, networkId, IntentEvent.class);
 
         this.store = serviceDirectory.get(VirtualNetworkStore.class);
+        this.intentStore = serviceDirectory.get(VirtualNetworkIntentStore.class);
         this.intentService = serviceDirectory.get(IntentService.class);
         this.partitionService = serviceDirectory.get(WorkPartitionService.class);
     }
@@ -117,7 +120,7 @@
     private boolean validateConnectPoint(ConnectPoint connectPoint) {
         checkNotNull(connectPoint, CP_NULL);
         Port port = getPort(connectPoint.deviceId(), connectPoint.port());
-        return port == null ? false : true;
+        return port != null;
     }
 
     /**
@@ -179,12 +182,12 @@
     @Override
     public Intent getIntent(Key key) {
         checkNotNull(key, KEY_NULL);
-        return store.getIntent(key);
+        return intentStore.getIntent(networkId, key);
     }
 
     @Override
     public Iterable<Intent> getIntents() {
-        return store.getIntents();
+        return intentStore.getIntents(networkId);
     }
 
     @Override
@@ -194,7 +197,7 @@
 
     @Override
     public Iterable<IntentData> getIntentData() {
-        return store.getIntentData();
+        return intentStore.getIntentData(networkId, false, 0);
     }
 
     @Override
@@ -205,7 +208,7 @@
     @Override
     public IntentState getIntentState(Key intentKey) {
         checkNotNull(intentKey, KEY_NULL);
-        return Optional.ofNullable(store.getIntentData(intentKey))
+        return Optional.ofNullable(intentStore.getIntentData(networkId, intentKey))
                 .map(IntentData::state)
                 .orElse(null);
     }
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
index 96979f5..d57801b 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
@@ -40,7 +40,6 @@
 import org.onosproject.incubator.net.virtual.VirtualNetwork;
 import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
 import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
-import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
 import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
 import org.onosproject.incubator.net.virtual.VirtualNetworkService;
 import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
@@ -63,10 +62,7 @@
 import org.onosproject.net.flowobjective.FlowObjectiveService;
 import org.onosproject.net.group.GroupService;
 import org.onosproject.net.host.HostService;
-import org.onosproject.net.intent.IntentEvent;
-import org.onosproject.net.intent.IntentListener;
 import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.provider.AbstractListenerProviderRegistry;
@@ -112,9 +108,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
-    private final InternalVirtualIntentListener intentListener =
-            new InternalVirtualIntentListener();
-
     private VirtualNetworkStoreDelegate delegate = this::post;
 
     private ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
@@ -147,7 +140,6 @@
         eventDispatcher.addSink(VirtualEvent.class,
                                 VirtualListenerRegistryManager.getInstance());
         store.setDelegate(delegate);
-        intentService.addListener(intentListener);
         appId = coreService.registerApplication(VIRTUAL_NETWORK_APP_ID_STRING);
         log.info("Started");
     }
@@ -157,7 +149,6 @@
         store.unsetDelegate(delegate);
         eventDispatcher.removeSink(VirtualNetworkEvent.class);
         eventDispatcher.removeSink(VirtualEvent.class);
-        intentService.removeListener(intentListener);
         log.info("Stopped");
     }
 
@@ -517,64 +508,12 @@
         }
     }
 
-    /**
-     * Internal intent event listener.
-     */
-    private class InternalVirtualIntentListener implements IntentListener {
-
-        @Override
-        public void event(IntentEvent event) {
-
-            // Ignore intent events that are not relevant.
-            if (!isRelevant(event)) {
-                return;
-            }
-
-            VirtualNetworkIntent intent = (VirtualNetworkIntent) event.subject();
-
-            switch (event.type()) {
-                case INSTALL_REQ:
-                    store.addOrUpdateIntent(intent, IntentState.INSTALL_REQ);
-                    break;
-                case INSTALLED:
-                    store.addOrUpdateIntent(intent, IntentState.INSTALLED);
-                    break;
-                case WITHDRAW_REQ:
-                    store.addOrUpdateIntent(intent, IntentState.WITHDRAW_REQ);
-                    break;
-                case WITHDRAWN:
-                    store.addOrUpdateIntent(intent, IntentState.WITHDRAWN);
-                    break;
-                case FAILED:
-                    store.addOrUpdateIntent(intent, IntentState.FAILED);
-                    break;
-                case CORRUPT:
-                    store.addOrUpdateIntent(intent, IntentState.CORRUPT);
-                    break;
-                case PURGED:
-                    store.removeIntent(intent.key());
-                default:
-                    break;
-            }
-        }
-
-        @Override
-        public boolean isRelevant(IntentEvent event) {
-            if (event.subject() instanceof VirtualNetworkIntent) {
-                return true;
-            }
-            return false;
-        }
-    }
-
-
     @Override
     protected VirtualNetworkProviderService
     createProviderService(VirtualNetworkProvider provider) {
         return new InternalVirtualNetworkProviderService(provider);
     }
 
-
     /**
      * Service issued to registered virtual network providers so that they
      * can interact with the core.
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
index 4b7cf84..3b59e9a 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
@@ -37,8 +37,10 @@
 import org.onosproject.incubator.net.virtual.VirtualLink;
 import org.onosproject.incubator.net.virtual.VirtualNetwork;
 import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
 import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
 import org.onosproject.incubator.store.virtual.impl.DistributedVirtualNetworkStore;
+import org.onosproject.incubator.store.virtual.impl.SimpleVirtualIntentStore;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultPort;
 import org.onosproject.net.EncapsulationType;
@@ -100,6 +102,7 @@
 
     private VirtualNetworkManager manager;
     private static DistributedVirtualNetworkStore virtualNetworkManagerStore;
+    private VirtualNetworkIntentStore intentStore;
     private CoreService coreService;
     private TestableIntentService intentService = new FakeIntentManager();
     private VirtualNetworkIntentManager vnetIntentService;
@@ -117,6 +120,7 @@
     @Before
     public void setUp() throws Exception {
         virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
+        intentStore = new SimpleVirtualIntentStore();
 
         coreService = new VirtualNetworkIntentManagerTest.TestCoreService();
 
@@ -215,7 +219,7 @@
 
         vnetIntentService = new VirtualNetworkIntentManager(manager, virtualNetwork.id());
         vnetIntentService.intentService = intentService;
-        vnetIntentService.store = virtualNetworkManagerStore;
+        vnetIntentService.intentStore = intentStore;
         vnetIntentService.partitionService = workPartitionService;
         return virtualNetwork;
     }
@@ -347,12 +351,12 @@
             switch (event.type()) {
                 case INSTALLED:
                     // Release one permit on the created semaphore since the Intent event was received.
-                    virtualNetworkManagerStore.addOrUpdateIntent(event.subject(), IntentState.INSTALLED);
+//                    virtualNetworkManagerStore.addOrUpdateIntent(event.subject(), IntentState.INSTALLED);
                     created.release();
                     break;
                 case WITHDRAWN:
                     // Release one permit on the removed semaphore since the Intent event was received.
-                    virtualNetworkManagerStore.addOrUpdateIntent(event.subject(), IntentState.WITHDRAWN);
+//                    virtualNetworkManagerStore.addOrUpdateIntent(event.subject(), IntentState.WITHDRAWN);
                     withdrawn.release();
                     break;
                 case PURGED:
@@ -396,4 +400,15 @@
             super(APP_ID, Collections.singletonList(new IntentTestsMocks.MockFlowRule(100)), Collections.emptyList());
         }
     }
+
+//    private void addOrUpdateIntent(Intent intent, IntentState state) {
+//        checkNotNull(intent, "Intent cannot be null");
+//        IntentData intentData = intentStore.(intent.key());
+//        if (intentData == null) {
+//            intentData = new IntentData(intent, state, new WallClockTimestamp(System.currentTimeMillis()));
+//        } else {
+//            intentData = new IntentData(intent, state, intentData.version());
+//        }
+//        intentKeyIntentDataMap.put(intent.key(), intentData);
+//    }
 }
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
index 765b4cb..9d4086e 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
@@ -71,7 +71,6 @@
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.intent.FakeIntentManager;
 import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MockIdGenerator;
 import org.onosproject.net.intent.TestableIntentService;
@@ -684,57 +683,6 @@
         assertTrue("The virtual network set should be empty.", virtualNetworks.isEmpty());
     }
 
-
-    /**
-     * Tests the addOrUpdateIntent() method in the store with a null intent.
-     */
-    @Test(expected = NullPointerException.class)
-    public void testAddOrUpdateNullIntent() {
-        manager.store.addOrUpdateIntent(null, null);
-    }
-
-    /**
-     * Tests the removeIntent() method in the store with a null intent key.
-     */
-    @Test(expected = NullPointerException.class)
-    public void testRemoveNullIntentKey() {
-        manager.store.removeIntent(null);
-    }
-
-    /**
-     * Tests the addOrUpdateIntent(), getIntents(), getIntent(), removeIntent() methods with the store.
-     */
-    @Test
-    public void testAddOrUpdateIntent() {
-        manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
-        VirtualNetwork virtualNetwork =
-                manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
-        ConnectPoint cp1 = new ConnectPoint(DID1, P1);
-        ConnectPoint cp2 = new ConnectPoint(DID2, P1);
-
-        VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
-                .networkId(virtualNetwork.id())
-                .key(Key.of("Test", APP_ID))
-                .appId(APP_ID)
-                .ingressPoint(cp1)
-                .egressPoint(cp2)
-                .build();
-
-        // Add the intent to the store.
-        manager.store.addOrUpdateIntent(virtualIntent, IntentState.INSTALL_REQ);
-        assertEquals("The intent size should match.", 1,
-                     manager.store.getIntents().size());
-        assertNotNull("The intent should not be null.",
-                      manager.store.getIntent(virtualIntent.key()));
-
-        // remove the intent from the store.
-        manager.store.removeIntent(virtualIntent.key());
-        assertTrue("The intents should be empty.",
-                   manager.store.getIntents().isEmpty());
-        assertNull("The intent should be null.",
-                   manager.store.getIntent(virtualIntent.key()));
-    }
-
     /**
      * Tests the addTunnelId() method in the store with a null intent.
      */
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
index 6447916..c938814 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
@@ -55,8 +55,6 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentData;
-import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
 import org.onosproject.store.AbstractStore;
 import org.onosproject.store.serializers.KryoNamespaces;
@@ -167,10 +165,6 @@
     private ConsistentMap<NetworkId, Set<VirtualPort>> networkIdVirtualPortSetConsistentMap;
     private Map<NetworkId, Set<VirtualPort>> networkIdVirtualPortSetMap;
 
-    // Track intent key to intent data
-    private ConsistentMap<Key, IntentData> intentKeyIntentDataConsistentMap;
-    private Map<Key, IntentData> intentKeyIntentDataMap;
-
     // Track intent ID to TunnelIds
     private ConsistentMap<Key, Set<TunnelId>> intentKeyTunnelIdSetConsistentMap;
     private Map<Key, Set<TunnelId>> intentKeyTunnelIdSetMap;
@@ -192,7 +186,6 @@
                            .register(DefaultVirtualPort.class)
                            .register(Device.class)
                            .register(TunnelId.class)
-                           .register(IntentData.class)
                            .register(VirtualNetworkIntent.class)
                            .register(WallClockTimestamp.class)
                            .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
@@ -278,13 +271,6 @@
                 .build();
         intentKeyTunnelIdSetMap = intentKeyTunnelIdSetConsistentMap.asJavaMap();
 
-        intentKeyIntentDataConsistentMap = storageService.<Key, IntentData>consistentMapBuilder()
-                .withSerializer(SERIALIZER)
-                .withName("onos-intentKey-intentData")
-                .withRelaxedReadConsistency()
-                .build();
-        intentKeyIntentDataMap = intentKeyIntentDataConsistentMap.asJavaMap();
-
         log.info("Started");
     }
 
@@ -798,24 +784,6 @@
     }
 
     @Override
-    public synchronized void addOrUpdateIntent(Intent intent, IntentState state) {
-        checkNotNull(intent, "Intent cannot be null");
-        IntentData intentData = removeIntent(intent.key());
-        if (intentData == null) {
-            intentData = new IntentData(intent, state, new WallClockTimestamp(System.currentTimeMillis()));
-        } else {
-            intentData = new IntentData(intent, state, intentData.version());
-        }
-        intentKeyIntentDataMap.put(intent.key(), intentData);
-    }
-
-    @Override
-    public IntentData removeIntent(Key intentKey) {
-        checkNotNull(intentKey, "Intent key cannot be null");
-        return intentKeyIntentDataMap.remove(intentKey);
-    }
-
-    @Override
     public void addTunnelId(Intent intent, TunnelId tunnelId) {
         // Add the tunnelId to the intent key set map
         Set<TunnelId> tunnelIdSet = intentKeyTunnelIdSetMap.remove(intent.key());
@@ -852,30 +820,6 @@
         }
     }
 
-    @Override
-    public Set<Intent> getIntents() {
-        Set<Intent> intents = new HashSet<>();
-        intentKeyIntentDataMap.values().forEach(intentData -> intents.add(intentData.intent()));
-        return ImmutableSet.copyOf(intents);
-    }
-
-    @Override
-    public Intent getIntent(Key key) {
-        IntentData intentData = intentKeyIntentDataMap.get(key);
-        return intentData == null ? null : intentData.intent();
-    }
-
-    @Override
-    public Set<IntentData> getIntentData() {
-        return ImmutableSet.copyOf(intentKeyIntentDataMap.values());
-    }
-
-    @Override
-    public IntentData getIntentData(Key key) {
-        IntentData intentData = intentKeyIntentDataMap.get(key);
-        return intentData ==  null ? null : new IntentData(intentData);
-    }
-
     /**
      * Listener class to map listener set events to the virtual network events.
      */
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/SimpleVirtualIntentStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/SimpleVirtualIntentStore.java
new file mode 100644
index 0000000..7f739b1
--- /dev/null
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/SimpleVirtualIntentStore.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.store.virtual.impl;
+
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.Key;
+import org.onosproject.store.Timestamp;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.PURGE_REQ;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Simple single-instance implementation of the intent store for virtual networks.
+ */
+
+@Component(immediate = true)
+@Service
+public class SimpleVirtualIntentStore
+        extends AbstractVirtualStore<IntentEvent, IntentStoreDelegate>
+        implements VirtualNetworkIntentStore {
+
+    private final Logger log = getLogger(getClass());
+
+    private final Map<NetworkId, Map<Key, IntentData>> currentByNetwork =
+            Maps.newConcurrentMap();
+    private final Map<NetworkId, Map<Key, IntentData>> pendingByNetwork =
+            Maps.newConcurrentMap();
+
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+
+    @Override
+    public long getIntentCount(NetworkId networkId) {
+        return getCurrentMap(networkId).size();
+    }
+
+    @Override
+    public Iterable<Intent> getIntents(NetworkId networkId) {
+        return getCurrentMap(networkId).values().stream()
+                .map(IntentData::intent)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Iterable<IntentData> getIntentData(NetworkId networkId,
+                                              boolean localOnly, long olderThan) {
+        if (localOnly || olderThan > 0) {
+            long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns
+            final SystemClockTimestamp time = new SystemClockTimestamp(older);
+            return getCurrentMap(networkId).values().stream()
+                    .filter(data -> data.version().isOlderThan(time) &&
+                            (!localOnly || isMaster(networkId, data.key())))
+                    .collect(Collectors.toList());
+        }
+        return Lists.newArrayList(getCurrentMap(networkId).values());
+    }
+
+    @Override
+    public IntentState getIntentState(NetworkId networkId, Key intentKey) {
+        IntentData data = getCurrentMap(networkId).get(intentKey);
+        return (data != null) ? data.state() : null;
+    }
+
+    @Override
+    public List<Intent> getInstallableIntents(NetworkId networkId, Key intentKey) {
+        IntentData data = getCurrentMap(networkId).get(intentKey);
+        if (data != null) {
+            return data.installables();
+        }
+        return null;
+    }
+
+    @Override
+    public void write(NetworkId networkId, IntentData newData) {
+        checkNotNull(newData);
+
+        synchronized (this) {
+            // TODO this could be refactored/cleaned up
+            IntentData currentData = getCurrentMap(networkId).get(newData.key());
+            IntentData pendingData = getPendingMap(networkId).get(newData.key());
+
+            if (IntentData.isUpdateAcceptable(currentData, newData)) {
+                if (pendingData != null) {
+                    if (pendingData.state() == PURGE_REQ) {
+                        getCurrentMap(networkId).remove(newData.key(), newData);
+                    } else {
+                        getCurrentMap(networkId).put(newData.key(), new IntentData(newData));
+                    }
+
+                    if (pendingData.version().compareTo(newData.version()) <= 0) {
+                        // pendingData version is less than or equal to newData's
+                        // Note: a new update for this key could be pending (it's version will be greater)
+                        getPendingMap(networkId).remove(newData.key());
+                    }
+                }
+                IntentEvent.getEvent(newData).ifPresent(e -> notifyDelegate(networkId, e));
+            }
+        }
+    }
+
+    @Override
+    public void batchWrite(NetworkId networkId, Iterable<IntentData> updates) {
+        for (IntentData data : updates) {
+            write(networkId, data);
+        }
+    }
+
+    @Override
+    public Intent getIntent(NetworkId networkId, Key key) {
+        IntentData data = getCurrentMap(networkId).get(key);
+        return (data != null) ? data.intent() : null;
+    }
+
+    @Override
+    public IntentData getIntentData(NetworkId networkId, Key key) {
+        IntentData currentData = getCurrentMap(networkId).get(key);
+        if (currentData == null) {
+            return null;
+        }
+        return new IntentData(currentData);
+    }
+
+    @Override
+    public void addPending(NetworkId networkId, IntentData data) {
+        if (data.version() == null) { // recompiled intents will already have a version
+            data = new IntentData(data.intent(), data.state(), new SystemClockTimestamp());
+        }
+        synchronized (this) {
+            IntentData existingData = getPendingMap(networkId).get(data.key());
+            if (existingData == null ||
+                    // existing version is strictly less than data's version
+                    // Note: if they are equal, we already have the update
+                    // TODO maybe we should still make this <= to be safe?
+                    existingData.version().compareTo(data.version()) < 0) {
+                getPendingMap(networkId).put(data.key(), data);
+
+                checkNotNull(delegateMap.get(networkId), "Store delegate is not set")
+                        .process(new IntentData(data));
+                IntentEvent.getEvent(data).ifPresent(e -> notifyDelegate(networkId, e));
+            } else {
+                log.debug("IntentData {} is older than existing: {}",
+                          data, existingData);
+            }
+            //TODO consider also checking the current map at this point
+        }
+    }
+
+    @Override
+    public boolean isMaster(NetworkId networkId, Key intentKey) {
+        return true;
+    }
+
+    @Override
+    public Iterable<Intent> getPending(NetworkId networkId) {
+        return getPendingMap(networkId).values().stream()
+                .map(IntentData::intent)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Iterable<IntentData> getPendingData(NetworkId networkId) {
+        return Lists.newArrayList(getPendingMap(networkId).values());
+    }
+
+    @Override
+    public IntentData getPendingData(NetworkId networkId, Key intentKey) {
+        return getPendingMap(networkId).get(intentKey);
+    }
+
+    @Override
+    public Iterable<IntentData> getPendingData(NetworkId networkId,
+                                               boolean localOnly, long olderThan) {
+        long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns
+        final SystemClockTimestamp time = new SystemClockTimestamp(older);
+        return getPendingMap(networkId).values().stream()
+                .filter(data -> data.version().isOlderThan(time) &&
+                        (!localOnly || isMaster(networkId, data.key())))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Returns the current intent map for a specific virtual network.
+     *
+     * @param networkId a virtual network identifier
+     * @return the current map for the requested virtual network
+     */
+    private Map<Key, IntentData> getCurrentMap(NetworkId networkId) {
+        currentByNetwork.computeIfAbsent(networkId,
+                                   n -> Maps.newConcurrentMap());
+        return currentByNetwork.get(networkId);
+    }
+
+    /**
+     * Returns the pending intent map for a specific virtual network.
+     *
+     * @param networkId a virtual network identifier
+     * @return the pending intent map for the requested virtual network
+     */
+    private Map<Key, IntentData> getPendingMap(NetworkId networkId) {
+        pendingByNetwork.computeIfAbsent(networkId,
+                                   n -> Maps.newConcurrentMap());
+        return pendingByNetwork.get(networkId);
+    }
+
+    public class SystemClockTimestamp implements Timestamp {
+
+        private final long nanoTimestamp;
+
+        public SystemClockTimestamp() {
+            nanoTimestamp = System.nanoTime();
+        }
+
+        public SystemClockTimestamp(long timestamp) {
+            nanoTimestamp = timestamp;
+        }
+
+        @Override
+        public int compareTo(Timestamp o) {
+            checkArgument(o instanceof SystemClockTimestamp,
+                          "Must be SystemClockTimestamp", o);
+            SystemClockTimestamp that = (SystemClockTimestamp) o;
+
+            return ComparisonChain.start()
+                    .compare(this.nanoTimestamp, that.nanoTimestamp)
+                    .result();
+        }
+    }
+}