Refactor external peer router store, fix NPE due to MAC is not ready

Change-Id: Id0381d9d1d7e0888dfbf1fc20acdd44d0a303e4c
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
index c02d964..2cebd2f 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
@@ -17,9 +17,13 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
@@ -54,6 +58,7 @@
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Objects;
@@ -64,6 +69,10 @@
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_CREATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_MAC_UPDATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_REMOVED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_UPDATED;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_CREATED;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_REMOVED;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_UPDATED;
@@ -112,6 +121,16 @@
             .register(LinkedHashMap.class)
             .build();
 
+    private static final KryoNamespace
+            SERIALIZER_EXTERNAL_PEER_ROUTER_MAP = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(ExternalPeerRouter.class)
+            .register(DefaultExternalPeerRouter.class)
+            .register(MacAddress.class)
+            .register(IpAddress.class)
+            .register(VlanId.class)
+            .build();
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
 
@@ -127,7 +146,10 @@
                         subnetMapListener = new OpenstackSubnetMapListener();
     private final MapEventListener<String, Port>
                         portMapListener = new OpenstackPortMapListener();
+    private final MapEventListener<String, ExternalPeerRouter>
+                        peerRouterListener = new ExternalPeerRouterMapListener();
 
+    private ConsistentMap<String, ExternalPeerRouter> externalPeerRouterStore;
     private ConsistentMap<String, Network> osNetworkStore;
     private ConsistentMap<String, Subnet> osSubnetStore;
     private ConsistentMap<String, Port> osPortStore;
@@ -157,6 +179,13 @@
                 .build();
         osPortStore.addListener(portMapListener);
 
+        externalPeerRouterStore = storageService.<String, ExternalPeerRouter>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_EXTERNAL_PEER_ROUTER_MAP))
+                .withName("external-routermap")
+                .withApplicationId(appId)
+                .build();
+        externalPeerRouterStore.addListener(peerRouterListener);
+
         log.info("Started");
     }
 
@@ -165,6 +194,7 @@
         osNetworkStore.removeListener(networkMapListener);
         osSubnetStore.removeListener(subnetMapListener);
         osPortStore.removeListener(portMapListener);
+        externalPeerRouterStore.removeListener(peerRouterListener);
         eventExecutor.shutdown();
 
         log.info("Stopped");
@@ -273,10 +303,48 @@
     }
 
     @Override
+    public ExternalPeerRouter externalPeerRouter(String ipAddress) {
+        return externalPeerRouterStore.asJavaMap().get(ipAddress);
+    }
+
+    @Override
+    public Set<ExternalPeerRouter> externalPeerRouters() {
+        return new HashSet<>(externalPeerRouterStore.asJavaMap().values());
+    }
+
+    @Override
+    public void createExternalPeerRouter(ExternalPeerRouter peerRouter) {
+        externalPeerRouterStore.compute(
+                peerRouter.ipAddress().toString(), (id, existing) -> {
+            final String error = peerRouter.ipAddress().toString() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return peerRouter;
+        });
+    }
+
+    @Override
+    public void updateExternalPeerRouter(ExternalPeerRouter peerRouter) {
+        externalPeerRouterStore.compute(
+                peerRouter.ipAddress().toString(), (id, existing) -> {
+            final String error = peerRouter.ipAddress() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return peerRouter;
+        });
+    }
+
+    @Override
+    public ExternalPeerRouter removeExternalPeerRouter(String ipAddress) {
+        Versioned<ExternalPeerRouter> peerRouter =
+                externalPeerRouterStore.remove(ipAddress);
+        return peerRouter == null ? null : peerRouter.value();
+    }
+
+    @Override
     public void clear() {
         osPortStore.clear();
         osSubnetStore.clear();
         osNetworkStore.clear();
+        externalPeerRouterStore.clear();
     }
 
     private class OpenstackNetworkMapListener
@@ -367,6 +435,62 @@
         }
     }
 
+    private class ExternalPeerRouterMapListener
+            implements MapEventListener<String, ExternalPeerRouter> {
+
+        @Override
+        public void event(MapEvent<String, ExternalPeerRouter> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    eventExecutor.execute(() -> processPeerRouterUpdate(event));
+                    break;
+                case INSERT:
+                    eventExecutor.execute(() -> processPeerRouterInsertion(event));
+                    break;
+                case REMOVE:
+                    eventExecutor.execute(() -> processPeerRouterRemoval(event));
+                    break;
+                default:
+                    log.error("Unsupported external peer router event type");
+                    break;
+            }
+        }
+
+        private void processPeerRouterUpdate(
+                MapEvent<String, ExternalPeerRouter> event) {
+            log.debug("External peer router updated");
+            notifyDelegate(new OpenstackNetworkEvent(
+                    EXTERNAL_PEER_ROUTER_UPDATED, event.newValue().value()));
+
+            processPeerRouterMacUpdated(event);
+        }
+
+        private void processPeerRouterInsertion(
+                MapEvent<String, ExternalPeerRouter> event) {
+            log.debug("External peer router inserted");
+            notifyDelegate(new OpenstackNetworkEvent(
+                    EXTERNAL_PEER_ROUTER_CREATED, event.newValue().value()));
+        }
+
+        private void processPeerRouterRemoval(
+                MapEvent<String, ExternalPeerRouter> event) {
+            log.debug("External peer router removed");
+            notifyDelegate(new OpenstackNetworkEvent(
+                    EXTERNAL_PEER_ROUTER_REMOVED, event.oldValue().value()));
+        }
+
+        private void processPeerRouterMacUpdated(
+                MapEvent<String, ExternalPeerRouter> event) {
+            ExternalPeerRouter oldPeerRouter = event.oldValue().value();
+            ExternalPeerRouter newPeerRouter = event.newValue().value();
+
+            if (!Objects.equals(oldPeerRouter.macAddress(), newPeerRouter.macAddress())) {
+                notifyDelegate(new OpenstackNetworkEvent(
+                        EXTERNAL_PEER_ROUTER_MAC_UPDATED, newPeerRouter));
+            }
+        }
+     }
+
     private class OpenstackPortMapListener implements MapEventListener<String, Port> {
 
         @Override