Fix for ONOS-4621. If any errors in parsing the configurations, continue with the next config key and return the list of subjectClass, subject, config failures

Change-Id: I4883342b4920aa4d6d641a17a395e5f6e4f27d6a
diff --git a/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java b/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
index 8921716..afff6a4 100644
--- a/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
+++ b/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
@@ -73,13 +73,14 @@
 
                 populateConfigurations();
 
-                applyConfigurations();
-
-                log.info("Loaded initial network configuration from {}", CFG_FILE);
+                if (applyConfigurations()) {
+                    log.info("Loaded initial network configuration from {}", CFG_FILE);
+                } else {
+                    log.error("Partially loaded initial network configuration from {}", CFG_FILE);
+                }
             }
         } catch (Exception e) {
-            log.warn("Unable to load initial network configuration from {}",
-                    CFG_FILE, e);
+            log.warn("Unable to load initial network configuration from {}", CFG_FILE, e);
         }
     }
 
@@ -185,8 +186,10 @@
     /**
      * Apply the configurations associated with all of the config classes that
      * are imported and have not yet been applied.
+     *
+     * @return false if any of the configuration parsing fails
      */
-    private void applyConfigurations() {
+    private boolean applyConfigurations() {
         Iterator<Map.Entry<InnerConfigPosition, JsonNode>> iter = jsons.entrySet().iterator();
 
         Map.Entry<InnerConfigPosition, JsonNode> entry;
@@ -195,7 +198,7 @@
         String subjectKey;
         String subjectString;
         String configKey;
-
+        boolean isSuccess = true;
         while (iter.hasNext()) {
             entry = iter.next();
             node = entry.getValue();
@@ -212,13 +215,19 @@
                 Object subject = networkConfigService.getSubjectFactory(subjectKey).
                         createSubject(subjectString);
 
-                //Apply the configuration
-                networkConfigService.applyConfig(subject, configClass, node);
+                try {
+                    //Apply the configuration
+                    networkConfigService.applyConfig(subject, configClass, node);
+                } catch (IllegalArgumentException e) {
+                    log.warn("Error parsing config " + subjectKey + "/" + subject + "/" + configKey);
+                    isSuccess = false;
+                }
 
                 //Now that it has been applied the corresponding JSON entry is no longer needed
                 iter.remove();
             }
         }
-    }
+        return isSuccess;
+   }
 
 }
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
index fc0b55d..b8dbb09 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
@@ -16,6 +16,7 @@
 package org.onosproject.rest.resources;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.NetworkConfigService;
@@ -33,6 +34,8 @@
 import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 import static org.onlab.util.Tools.emptyIsNotFound;
@@ -44,6 +47,9 @@
 @Path("network/configuration")
 public class NetworkConfigWebResource extends AbstractWebResource {
 
+    //FIX ME not found Multi status error code 207 in jaxrs Response Status.
+    private static final int  MULTI_STATUS_RESPONE = 207;
+
     private String subjectClassNotFoundErrorString(String subjectClassKey) {
         return "Config for '" + subjectClassKey + "' not found";
     }
@@ -191,9 +197,16 @@
     public Response upload(InputStream request) throws IOException {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
+        List<String> errorMsgs = new ArrayList<String>();
         root.fieldNames()
-                .forEachRemaining(sk -> consumeJson(service, (ObjectNode) root.path(sk),
-                                                    service.getSubjectFactory(sk)));
+                .forEachRemaining(sk ->
+                {
+                    errorMsgs.addAll(consumeJson(service, (ObjectNode) root.path(sk),
+                                                 service.getSubjectFactory(sk)));
+                });
+        if (errorMsgs.toString().length() > 0) {
+            return Response.status(MULTI_STATUS_RESPONE).entity(produceErrorJson(errorMsgs)).build();
+        }
         return Response.ok().build();
     }
 
@@ -213,7 +226,10 @@
                            InputStream request) throws IOException {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
-        consumeJson(service, root, service.getSubjectFactory(subjectClassKey));
+        List<String> errorMsgs = consumeJson(service, root, service.getSubjectFactory(subjectClassKey));
+        if (errorMsgs.toString().length() > 0) {
+            return Response.status(MULTI_STATUS_RESPONE).entity(produceErrorJson(errorMsgs)).build();
+        }
         return Response.ok().build();
     }
 
@@ -235,9 +251,12 @@
                            InputStream request) throws IOException {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
-        consumeSubjectJson(service, root,
-                           service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
-                           subjectClassKey);
+        List<String> errorMsgs = consumeSubjectJson(service, root,
+                                 service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+                                 subjectClassKey);
+        if (errorMsgs.size() > 0) {
+            return Response.status(MULTI_STATUS_RESPONE).entity(produceErrorJson(errorMsgs)).build();
+        }
         return Response.ok().build();
     }
 
@@ -267,21 +286,37 @@
         return Response.ok().build();
     }
 
-    private void consumeJson(NetworkConfigService service, ObjectNode classNode,
+    private List<String> consumeJson(NetworkConfigService service, ObjectNode classNode,
                              SubjectFactory subjectFactory) {
-        classNode.fieldNames().forEachRemaining(s ->
-            consumeSubjectJson(service, (ObjectNode) classNode.path(s),
-                               subjectFactory.createSubject(s),
-                               subjectFactory.subjectClassKey()));
+        List<String> errorMsgs = new ArrayList<String>();
+        classNode.fieldNames().forEachRemaining(s -> {
+            List<String> error = consumeSubjectJson(service, (ObjectNode) classNode.path(s),
+                                                    subjectFactory.createSubject(s),
+                                                    subjectFactory.subjectClassKey());
+            errorMsgs.addAll(error);
+        });
+        return errorMsgs;
     }
 
-    private void consumeSubjectJson(NetworkConfigService service,
+    private List<String> consumeSubjectJson(NetworkConfigService service,
                                     ObjectNode subjectNode, Object subject,
                                     String subjectClassKey) {
-        subjectNode.fieldNames().forEachRemaining(configKey ->
-            service.applyConfig(subjectClassKey, subject, configKey, subjectNode.path(configKey)));
+        List<String> errorMsgs = new ArrayList<String>();
+        subjectNode.fieldNames().forEachRemaining(configKey -> {
+            try {
+                service.applyConfig(subjectClassKey, subject, configKey, subjectNode.path(configKey));
+            } catch (IllegalArgumentException e) {
+                errorMsgs.add("Error parsing config " + subjectClassKey + "/" + subject + "/" + configKey);
+            }
+        });
+        return errorMsgs;
     }
 
+    private ObjectNode produceErrorJson(List<String> errorMsgs) {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode result = mapper.createObjectNode().put("code", 207).putPOJO("message", errorMsgs);
+        return result;
+    }
 
     // FIXME: Refactor to allow queued configs to be removed