ONOS-3379: Config REST 500 errors on GET operations if keys don't exist

Change-Id: Ie32bdb70693c5571421840265b4be71d0706d797
diff --git a/utils/misc/src/main/java/org/onlab/util/Tools.java b/utils/misc/src/main/java/org/onlab/util/Tools.java
index 1b78814..ffefbfd 100644
--- a/utils/misc/src/main/java/org/onlab/util/Tools.java
+++ b/utils/misc/src/main/java/org/onlab/util/Tools.java
@@ -40,6 +40,7 @@
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
@@ -144,6 +145,23 @@
     }
 
     /**
+     * Returns the specified set if the set is not null and not empty;
+     * otherwise throws a not found exception.
+     *
+     * @param item set to check
+     * @param message not found message
+     * @param <T> Set item type
+     * @return item if not null and not empty
+     * @throws org.onlab.util.ItemNotFoundException if set is null or empty
+     */
+    public static <T> Set<T> emptyIsNotFound(Set<T> item, String message) {
+        if (item == null || item.isEmpty()) {
+            throw new ItemNotFoundException(message);
+        }
+        return item;
+    }
+
+    /**
      * Returns the specified item if that item is not null; otherwise throws
      * bad argument exception.
      *
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 954b25a..69c0b6a 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
@@ -15,10 +15,9 @@
  */
 package org.onosproject.rest.resources;
 
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.config.SubjectFactory;
-import org.onosproject.rest.AbstractWebResource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -29,8 +28,16 @@
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import java.io.IOException;
-import java.io.InputStream;
+
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.SubjectFactory;
+import org.onosproject.rest.AbstractWebResource;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static org.onlab.util.Tools.emptyIsNotFound;
+import static org.onlab.util.Tools.nullIsNotFound;
 
 /**
  * Manage network configurations.
@@ -38,8 +45,29 @@
 @Path("network/configuration")
 public class NetworkConfigWebResource extends AbstractWebResource {
 
+
+    private String subjectClassNotFoundErrorString(String subjectClassKey) {
+        return "Config for '" + subjectClassKey + "' not found";
+    }
+
+    private String subjectNotFoundErrorString(String subjectClassKey,
+                                              String subjectKey) {
+        return "Config for '"
+                + subjectClassKey + "/" + subjectKey
+                + "' not found";
+    }
+
+    private String configKeyNotFoundErrorString(String subjectClassKey,
+                                                String subjectKey,
+                                                String configKey) {
+        return "Config for '"
+                + subjectClassKey + "/" + subjectKey + "/" + configKey
+                + "' not found";
+    }
+
     /**
      * Get entire network configuration base.
+     *
      * @rsModel NetCfgGet
      * @return network configuration JSON
      */
@@ -70,7 +98,9 @@
     public Response download(@PathParam("subjectClassKey") String subjectClassKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = mapper().createObjectNode();
-        SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey);
+        SubjectFactory subjectFactory =
+                nullIsNotFound(service.getSubjectFactory(subjectClassKey),
+                               subjectClassNotFoundErrorString(subjectClassKey));
         produceJson(service, root, subjectFactory, subjectFactory.subjectClass());
         return ok(root).build();
     }
@@ -90,8 +120,12 @@
                              @PathParam("subjectKey") String subjectKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = mapper().createObjectNode();
-        SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey);
-        produceSubjectJson(service, root, subjectFactory.createSubject(subjectKey));
+        SubjectFactory subjectFactory =
+                nullIsNotFound(service.getSubjectFactory(subjectClassKey),
+                               subjectClassNotFoundErrorString(subjectClassKey));
+        produceSubjectJson(service, root, subjectFactory.createSubject(subjectKey),
+                           true,
+                           subjectNotFoundErrorString(subjectClassKey, subjectKey));
         return ok(root).build();
     }
 
@@ -111,20 +145,40 @@
                              @PathParam("subjectKey") String subjectKey,
                              @PathParam("configKey") String configKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
-        return ok(service.getConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
-                                    service.getConfigClass(subjectClassKey, configKey)).node()).build();
+
+        Object subject =
+                nullIsNotFound(service.getSubjectFactory(subjectClassKey)
+                                       .createSubject(subjectKey),
+                                        subjectNotFoundErrorString(subjectClassKey, subjectKey));
+
+        Class configClass =
+                nullIsNotFound(service.getConfigClass(subjectClassKey, configKey),
+                               configKeyNotFoundErrorString(subjectClassKey, subjectKey, configKey));
+        Config config =
+                nullIsNotFound(service.getConfig(subject, configClass),
+                               configKeyNotFoundErrorString(subjectClassKey,
+                                                            subjectKey,
+                                                            configKey));
+        return ok(config.node()).build();
     }
 
     @SuppressWarnings("unchecked")
     private void produceJson(NetworkConfigService service, ObjectNode node,
                              SubjectFactory subjectFactory, Class subjectClass) {
         service.getSubjects(subjectClass).forEach(s ->
-            produceSubjectJson(service, newObject(node, subjectFactory.subjectKey(s)), s));
+            produceSubjectJson(service, newObject(node, subjectFactory.subjectKey(s)), s, false, ""));
     }
 
     private void produceSubjectJson(NetworkConfigService service, ObjectNode node,
-                                    Object subject) {
-        service.getConfigs(subject).forEach(c -> node.set(c.key(), c.node()));
+                                    Object subject,
+                                    boolean emptyIsError,
+                                    String emptyErrorMessage) {
+        Set<? extends Config<Object>> configs = service.getConfigs(subject);
+        if (emptyIsError) {
+            // caller wants an empty set to be a 404
+            configs = emptyIsNotFound(configs, emptyErrorMessage);
+        }
+        configs.forEach(c -> node.set(c.key(), c.node()));
     }