CORD-61 Dynamic XConnect support

- Add new XConnectConfig with unit test
- Gather XConnect features into XConnectHandler
- Introduce ObjectiveError.Type.GROUPREMOVALFAILED
- Rename
    - NetworkConfigEventHandler -> AppConfigHandler
    - XConnectNextObjectiveStoreKey -> XConnectStoreKey
    - Test json file
- Refactor

Change-Id: I8ca3176ed976c71ce9e28b7f3722ce80d49c816f
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java
index b260751..6d5666a 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java
@@ -79,8 +79,8 @@
     private static final Logger log = LoggerFactory.getLogger(McastHandler.class);
     private final SegmentRoutingManager srManager;
     private final ApplicationId coreAppId;
-    private StorageService storageService;
-    private TopologyService topologyService;
+    private final StorageService storageService;
+    private final TopologyService topologyService;
     private final ConsistentMap<McastStoreKey, NextObjective> mcastNextObjStore;
     private final KryoNamespace.Builder mcastKryo;
     private final ConsistentMap<McastStoreKey, McastRole> mcastRoleStore;
@@ -132,7 +132,7 @@
     /**
      * Read initial multicast from mcast store.
      */
-    public void init() {
+    protected void init() {
         srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> {
             ConnectPoint source = srManager.multicastRouteService.fetchSource(mcastRoute);
             Set<ConnectPoint> sinks = srManager.multicastRouteService.fetchSinks(mcastRoute);
@@ -472,7 +472,7 @@
                             log.warn("Failed to update {} on {}/{}, vlan {}: {}",
                                     mcastIp, deviceId, port.toLong(), assignedVlan, error));
             newNextObj = nextObjBuilder(mcastIp, assignedVlan, existingPorts).add();
-            fwdObj = fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add();
+            fwdObj = fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add(context);
             mcastNextObjStore.put(mcastStoreKey, newNextObj);
             srManager.flowObjectiveService.next(deviceId, newNextObj);
             srManager.flowObjectiveService.forward(deviceId, fwdObj);
@@ -779,11 +779,7 @@
                 // Spine-facing port should have no subnet and no xconnect
                 if (srManager.deviceConfiguration != null &&
                         srManager.deviceConfiguration.getPortSubnet(ingressDevice, port) == null &&
-                        srManager.deviceConfiguration.getXConnects().values().stream()
-                                .allMatch(connectPoints ->
-                                        connectPoints.stream().noneMatch(connectPoint ->
-                                                connectPoint.port().equals(port))
-                                )) {
+                        !srManager.xConnectHandler.hasXConnect(new ConnectPoint(ingressDevice, port))) {
                     return port;
                 }
             }