Adding support for configurations added after the Net Config Loader has loaded.

Change-Id: I611f3b8f36805e2854485b694edffc8d93581c7e
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/config/impl/NetworkConfigLoader.java b/incubator/net/src/main/java/org/onosproject/incubator/net/config/impl/NetworkConfigLoader.java
index b6e24d5..6c569d0 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/config/impl/NetworkConfigLoader.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/config/impl/NetworkConfigLoader.java
@@ -17,16 +17,22 @@
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+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.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.incubator.net.config.NetworkConfigEvent;
+import org.onosproject.incubator.net.config.NetworkConfigListener;
 import org.onosproject.incubator.net.config.NetworkConfigService;
-import org.onosproject.incubator.net.config.SubjectFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * Component for loading the initial network configuration.
@@ -43,59 +49,132 @@
 
     // FIXME: Add mutual exclusion to make sure this happens only once per startup.
 
-    // TODO: add a field to track the collection of pending JSONS
+    private Map<InnerConfigPosition, ObjectNode> jsons = Maps.newHashMap();
+
+    private final NetworkConfigListener configListener = new InnerConfigListener();
+
+    ObjectNode root;
 
     @Activate
     public void activate() {
-        // Add listener to net config events
+        //TODO Maybe this should be at the bottom to avoid a potential race
+        networkConfigService.addListener(configListener);
         try {
             if (CFG_FILE.exists()) {
-                ObjectNode root = (ObjectNode) new ObjectMapper().readTree(CFG_FILE);
-                // Parse this JSON structure and accumulate a collection of all leaf config JSONs
+                root = (ObjectNode) new ObjectMapper().readTree(CFG_FILE);
 
-                // Perform initial iteration over all leaf configs and attempt to apply them,
-                // but do this only if they are valid.
-//                networkConfigService.getConfigClass("foo");
+                populateConfigurations();
 
-                // This code can be used for building the collection of jsons
-                root.fieldNames().forEachRemaining(sk ->
-                       consumeJson(networkConfigService, (ObjectNode) root.path(sk),
-                                   networkConfigService.getSubjectFactory(sk)));
+                applyConfigurations();
+
                 log.info("Loaded initial network configuration from {}", CFG_FILE);
             }
         } catch (Exception e) {
             log.warn("Unable to load initial network configuration from {}",
-                     CFG_FILE, e);
+                    CFG_FILE, e);
         }
     }
 
-
-    // TODO: add deactivate which will remove listener
-
-    // TODO: implement event listener and as each config is registered,
+    @Deactivate
+    public void deactivate() {
+        networkConfigService.removeListener(configListener);
+    }
     // sweep through pending config jsons and try to add them
 
     /**
-     * Consumes configuration JSON for the specified subject factory.
-     *
-     * @param service        network configuration service
-     * @param classNode      subject class JSON node
-     * @param subjectFactory subject factory
+     * Inner class that allows for handling of newly added NetConfig types.
      */
-    static void consumeJson(NetworkConfigService service, ObjectNode classNode,
-                            SubjectFactory subjectFactory) {
-        classNode.fieldNames().forEachRemaining(s ->
-                                                        consumeSubjectJson(service, (ObjectNode) classNode.path(s),
-                                                                           subjectFactory.createSubject(s),
-                                                                           subjectFactory.subjectKey()));
+    private final class InnerConfigListener implements NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            //TODO should this be done for other types of NetworkConfigEvents?
+            if (event.type() == NetworkConfigEvent.Type.CONFIG_REGISTERED ||
+                    event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
+                applyConfigurations();
+            }
+
+        }
     }
 
-    private static void consumeSubjectJson(NetworkConfigService service,
-                                           ObjectNode subjectNode, Object subject, String subjectKey) {
-        subjectNode.fieldNames().forEachRemaining(c ->
-                                                          service.applyConfig(subject,
-                                                                              service.getConfigClass(subjectKey, c),
-                                                                              (ObjectNode) subjectNode.path(c)));
+    /**
+     * Inner class that allows for tracking of JSON class configurations.
+     */
+    private final class InnerConfigPosition {
+        private String subjectKey, subject, classKey;
+
+        private String getSubjectKey() {
+            return subjectKey;
+        }
+
+        private String getSubject() {
+            return subject;
+        }
+
+        private String getClassKey() {
+            return classKey;
+        }
+
+        private InnerConfigPosition(String subjectKey, String subject, String classKey) {
+            this.subjectKey = subjectKey;
+            this.subject = subject;
+            this.classKey = classKey;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof InnerConfigPosition) {
+                final InnerConfigPosition that = (InnerConfigPosition) obj;
+                return Objects.equals(this.subjectKey, that.subjectKey) && Objects.equals(this.subject, that.subject)
+                        && Objects.equals(this.classKey, that.classKey);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(subjectKey, subject, classKey);
+        }
+    }
+
+    private void saveJson(String sk, ObjectNode node) {
+        node.fieldNames().forEachRemaining(s ->
+                saveSubjectJson(sk, s, (ObjectNode) node.path(s)));
+    }
+
+    private void saveSubjectJson(String sk,
+                                 String s, ObjectNode node) {
+        node.fieldNames().forEachRemaining(c ->
+                this.jsons.put(new InnerConfigPosition(sk, s, c), (ObjectNode) node.path(c)));
+    }
+
+    private void populateConfigurations() {
+        root.fieldNames().forEachRemaining(sk ->
+                saveJson(sk, (ObjectNode) root.path(sk)));
+
+    }
+
+    protected void applyConfigurations() {
+        Iterator<Map.Entry<InnerConfigPosition, ObjectNode>> iter = jsons.entrySet().iterator();
+        Map.Entry<InnerConfigPosition, ObjectNode> entry;
+        while (iter.hasNext()) {
+            entry = iter.next();
+            if (networkConfigService.getConfigClass(networkConfigService.getSubjectFactory(entry.getKey().
+                    getSubjectKey()).subjectKey(), entry.getKey().getSubject()) != null) {
+                networkConfigService.applyConfig(networkConfigService.getSubjectFactory(
+                                entry.getKey().getSubjectKey()).createSubject(entry.getKey().getSubject()),
+                        networkConfigService.getConfigClass(networkConfigService.getSubjectFactory(entry.getKey().
+                                getSubjectKey()).subjectKey(), entry.getKey().getClassKey()),
+                        (ObjectNode) root.path(entry.getKey().getSubjectKey()).
+                                path(entry.getKey().getSubject()).
+                                path(entry.getKey().getClassKey()));
+                jsons.remove(entry.getKey());
+            }
+
+        }
     }
 
 }