Supporting hybrid local + remote config

Change-Id: I2da729503fcbcaa2ae3e1ec99bc13bb046ef4f13
diff --git a/api/src/main/java/org/opencord/sadis/BaseInformationService.java b/api/src/main/java/org/opencord/sadis/BaseInformationService.java
index 9d2896b..53966ee 100644
--- a/api/src/main/java/org/opencord/sadis/BaseInformationService.java
+++ b/api/src/main/java/org/opencord/sadis/BaseInformationService.java
@@ -18,6 +18,11 @@
 public interface BaseInformationService<T extends BaseInformation> {
 
     /**
+     * Removes all local stored data.
+     */
+    void clearLocalData();
+
+    /**
      * Removes all cached entries.
      */
     void invalidateAll();
diff --git a/app/src/main/java/org/opencord/sadis/impl/InformationAdapter.java b/app/src/main/java/org/opencord/sadis/impl/InformationAdapter.java
index f13764a..7656cf0 100644
--- a/app/src/main/java/org/opencord/sadis/impl/InformationAdapter.java
+++ b/app/src/main/java/org/opencord/sadis/impl/InformationAdapter.java
@@ -36,6 +36,7 @@
 import org.opencord.sadis.BaseInformation;
 import org.opencord.sadis.BaseConfig;
 import org.opencord.sadis.BaseInformationService;
+
 import java.util.Set;
 
 
@@ -80,20 +81,18 @@
 
         String url = null;
         try {
-            // if the url is not present then assume data is in netcfg
             if (cfg.getUrl() != null) {
                 url = cfg.getUrl().toString();
-            } else {
-                localCfgData = Maps.newConcurrentMap();
-
-                cfg.getEntries().forEach(entry ->
-                        localCfgData.put(entry.id(), entry));
-                log.info("url is null, data source is local netcfg data");
             }
         } catch (MalformedURLException mUrlEx) {
             log.error("Invalid URL specified: {}", mUrlEx);
         }
 
+        // always load local data
+        localCfgData = Maps.newConcurrentMap();
+        cfg.getEntries().forEach(entry ->
+                localCfgData.put(entry.id(), entry));
+
         int maximumCacheSeize = cfg.getCacheMaxSize();
         long cacheEntryTtl = cfg.getCacheTtl().getSeconds();
 
@@ -128,6 +127,17 @@
      * (non-Javadoc)
      *
      * @see
+     * org.opencord.sadis.SadisService#clearLocalData()
+     */
+    @Override
+    public void clearLocalData() {
+        localCfgData.clear();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
      * org.opencord.sadis.SadisService#clearCache()
      */
     @Override
@@ -186,32 +196,42 @@
         }
 
         /*
-         * Not in cache, if we have a URL configured we can attempt to get it
-         * from there, else check for it in the locally configured data
+         * Not in cache, check for it in the locally configured data,
+         * if it's not there and we have a URL configured
+         * we can attempt to get it from there
          */
-        if (this.url == null) {
-            info = (localCfgData == null) ? null : localCfgData.get(id);
 
-            if (info != null) {
-                local.put(id, info);
-                return info;
+        log.debug("Getting data from local config");
+        info = (localCfgData == null) ? null : localCfgData.get(id);
+
+        if (log.isTraceEnabled()) {
+            if (info == null) {
+                log.trace("Data not found in local config.");
+            } else {
+                log.trace("Found data in local config.");
             }
-        } else {
+        }
+
+        if (info == null && this.url != null) {
             // Augment URL with query parameters
             String urlWithSubId = this.url.replaceAll("%s", id);
             log.debug("Getting data from the remote URL {}", urlWithSubId);
 
             try (InputStream io = new URL(urlWithSubId).openStream()) {
                 info = mapper.readValue(io, getInformationClass());
-                local.put(id, info);
-                return info;
             } catch (IOException e) {
                 // TODO use a better http library that allows us to read status code
                 log.debug("Exception while reading remote data {} ", e.getMessage());
             }
         }
-        log.warn("Data not found for id {}", id);
-        return null;
+
+        if (info != null) {
+            local.put(id, info);
+            return info;
+        } else {
+            log.warn("Data not found for id {}", id);
+            return null;
+        }
     }
 
     public abstract void registerModule();
diff --git a/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java
index fa68966..1fbfbcd 100644
--- a/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java
+++ b/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java
@@ -86,6 +86,7 @@
 
     @Test
     public void testRemoteMode() throws Exception {
+
         BaseInformationService<SubscriberAndDeviceInformation> subscriberService = sadis.getSubscriberInfoService();
         config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
         configListener.event(event);
@@ -106,6 +107,8 @@
         config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
         configListener.event(event);
 
+        service.clearLocalData();
+
         checkGetForExisting(ID3, null, service);
         checkGetForNonExist(ID1, service);
 
@@ -116,6 +119,23 @@
         checkGetForNonExist(ID3, service);
     }
 
+    // test the hybrid mode (both local and remote data in the config)
+    // ids 1 and 2 are local, others are remote
+    @Test
+    public void testHybridMode() throws Exception {
+        BaseInformationService<SubscriberAndDeviceInformation> subscriberService = sadis.getSubscriberInfoService();
+        config.init(subject, "sadis-hybrid-mode-test", node("/HybridSubConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        // check that I can fetch from remote
+        checkGetForExisting(ID3, entry3, subscriberService);
+        checkGetForExisting(ID4, entry4, subscriberService);
+
+        // check that I can fetch from local
+        checkGetForExisting(ID1, entry1, subscriberService);
+        checkGetForExisting(ID2, entry2, subscriberService);
+    }
+
     public boolean checkEquality(BaseInformation localEntry, BaseInformation entry) {
         SubscriberAndDeviceInformation sub = (SubscriberAndDeviceInformation) localEntry;
         SubscriberAndDeviceInformation other = (SubscriberAndDeviceInformation) localEntry;
diff --git a/app/src/test/resources/HybridSubConfig.json b/app/src/test/resources/HybridSubConfig.json
new file mode 100644
index 0000000..e88a414
--- /dev/null
+++ b/app/src/test/resources/HybridSubConfig.json
@@ -0,0 +1,84 @@
+{
+	"integration":
+	{
+		"url": "file:src/test/resources/%s",
+		"cache":
+		{
+			"maxsize": 50,
+			"ttl": "PT1m"
+		}
+	},
+
+	"entries":
+	[
+		{
+			"id": "1",
+			"nasPortId": "1/1/2",
+			"uplinkPort": 125,
+			"slot": 3,
+			"hardwareIdentifier": "aa:bb:cc:dd:ee:ff",
+			"ipAddress":"10.10.10.10",
+			"nasId":"XXX-NASID",
+			"circuitId":"circuit123",
+			"remoteId":"remote123",
+			"uniTagList": [
+				{
+					"uniTagMatch":100,
+					"ponCTag":"2,",
+					"ponSTag":"2",
+					"usPonCTagPriority":0,
+					"usPonSTagPriority":1,
+					"dsPonCTagPriority":0,
+					"dsPonSTagPriority":1,
+					"technologyProfileId":64,
+					"upstreamBandwidthProfile":"HSA",
+					"downstreamBandwidthProfile":"HSA",
+					"serviceName":"HSIA"
+				}
+			]
+		},
+
+		{
+			"id": "2",
+			"nasPortId": "1/1/2",
+			"uplinkPort": 129,
+			"slot": 4,
+			"hardwareIdentifier": "aa:bb:cc:dd:ee:ff",
+			"ipAddress":"1.1.1.1",
+			"nasId":"YYY-NASID",
+			"circuitId":"circuit234",
+			"remoteId":"remote234",
+			"uniTagList": [
+				{
+					"uniTagMatch":100,
+					"ponCTag":"2,",
+					"ponSTag":"2",
+					"usPonCTagPriority":0,
+					"usPonSTagPriority":1,
+					"dsPonCTagPriority":0,
+					"dsPonSTagPriority":1,
+					"technologyProfileId":64,
+					"upstreamBandwidthProfile":"HSA",
+					"downstreamBandwidthProfile":"HSA",
+					"serviceName":"HSIA"
+				},
+				{
+					"uniTagMatch":200,
+					"ponCTag":3,
+					"ponSTag":2,
+					"usPonCTagPriority":0,
+					"usPonSTagPriority":1,
+					"dsPonCTagPriority": 0,
+					"dsPonSTagPriority": 1,
+					"technologyProfileId":65,
+					"upstreamBandwidthProfile":"IPTV",
+					"downstreamBandwidthProfile":"IPTV",
+					"serviceName":"IPTV",
+					"isIgmpRequired": "true",
+					"isDhcpRequired": "true",
+					"configuredMacAddress": "\"ff:aa:dd:cc:bb:ee\""
+				}
+			]
+		}
+	]
+}