REST/CLI Implementations and Fix for remote url data

Change-Id: I0daeebdd40864179c6b923bed3da0c38f259829e
diff --git a/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformationService.java b/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformationService.java
index 3b07929..be0a42e 100644
--- a/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformationService.java
+++ b/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformationService.java
@@ -26,6 +26,13 @@
     public void invalidateAll();
 
     /**
+      * Removes the information associated with the given ID.
+      * @param id
+      *           key to information
+      */
+    public void invalidateId(String id);
+
+    /**
      * Return the information associated with the given ID.
      *
      * @param id
@@ -33,4 +40,14 @@
      * @return information associated with ID, if available, else null
      */
     public SubscriberAndDeviceInformation get(String id);
+
+    /**
+     * Return the information associated with the given ID from the cache.
+     *
+     * @param id
+     *            key to information
+     * @return information associated with ID, if available in the cache, else null
+     */
+    public SubscriberAndDeviceInformation getfromCache(String id);
+
 }
diff --git a/app/src/main/java/org/opencord/sadis/cli/SubscriberGetCommand.java b/app/src/main/java/org/opencord/sadis/cli/SubscriberGetCommand.java
index 42ec7bb..93e9830 100644
--- a/app/src/main/java/org/opencord/sadis/cli/SubscriberGetCommand.java
+++ b/app/src/main/java/org/opencord/sadis/cli/SubscriberGetCommand.java
@@ -18,6 +18,8 @@
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
+import org.opencord.sadis.SubscriberAndDeviceInformationService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
 
 /**
  * Subscriber And Device Information Service CLI.
@@ -28,9 +30,15 @@
     @Argument(index = 0, name = "ID", description = "subscriber ID", required = true, multiValued = false)
     String id;
 
+    private SubscriberAndDeviceInformationService sadisService = get(SubscriberAndDeviceInformationService.class);
+
     @Override
     protected void execute() {
-        print("Hello %s", "World");
+        SubscriberAndDeviceInformation info = sadisService.get(id);
+        if (info != null) {
+           print(info.toString());
+        } else {
+           print("Subscriber not found");
+        }
     }
-
 }
diff --git a/app/src/main/java/org/opencord/sadis/impl/SadisManager.java b/app/src/main/java/org/opencord/sadis/impl/SadisManager.java
index 1297235..f8e4de8 100644
--- a/app/src/main/java/org/opencord/sadis/impl/SadisManager.java
+++ b/app/src/main/java/org/opencord/sadis/impl/SadisManager.java
@@ -31,6 +31,8 @@
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
 import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.codec.CodecService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,6 +59,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigRegistry cfgService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CodecService codecService;
+
     @SuppressWarnings("rawtypes")
     private final Set<ConfigFactory> factories = ImmutableSet
             .of(new ConfigFactory<ApplicationId, SadisConfig>(APP_SUBJECT_FACTORY, SadisConfig.class, "sadis") {
@@ -73,6 +78,7 @@
     protected void activate() {
 
         this.appId = this.coreService.registerApplication(SADIS_APP);
+        codecService.registerCodec(SubscriberAndDeviceInformation.class, new SubscriberAndDeviceInformationCodec());
         this.cfgService.addListener(this.cfgListener);
         this.factories.forEach(this.cfgService::registerConfigFactory);
         this.updateConfig();
diff --git a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationAdapter.java b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationAdapter.java
index 8842490..fbd77aa 100644
--- a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationAdapter.java
+++ b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationAdapter.java
@@ -26,9 +26,12 @@
 import org.opencord.sadis.SubscriberAndDeviceInformationService;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.collect.Maps;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.Ip4Address;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,6 +45,7 @@
     private static final long DEFAULT_TTL = 0;
 
     private String url;
+    private ObjectMapper mapper;
     private Cache<String, SubscriberAndDeviceInformation> cache;
     private int maxiumCacheSize = DEFAULT_MAXIMUM_CACHE_SIZE;
     private long cacheEntryTtl = DEFAULT_TTL;
@@ -51,6 +55,14 @@
     public SubscriberAndDeviceInformationAdapter() {
         cache = CacheBuilder.newBuilder().maximumSize(maxiumCacheSize)
                 .expireAfterAccess(cacheEntryTtl, TimeUnit.SECONDS).build();
+        mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule();
+        SadisConfig config = new SadisConfig();
+        SadisConfig.VlanIdDeserializer vlanID = config.new VlanIdDeserializer();
+        SadisConfig.Ip4AddressDeserializer ip4Address = config.new Ip4AddressDeserializer();
+        module.addDeserializer(VlanId.class, vlanID);
+        module.addDeserializer(Ip4Address.class, ip4Address);
+        mapper.registerModule(module);
     }
 
     /**
@@ -114,6 +126,37 @@
      * (non-Javadoc)
      *
      * @see
+     * org.opencord.sadis.SubscriberAndDeviceInformationService#invalidateId()
+     */
+    @Override
+    public void invalidateId(String id) {
+        cache.invalidate(id);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opencord.sadis.SubscriberAndDeviceInformationService#getfromCache(java.lang.
+     * String)
+     */
+     @Override
+     public SubscriberAndDeviceInformation getfromCache(String id) {
+         Cache<String, SubscriberAndDeviceInformation> local;
+         synchronized (this) {
+           local = cache;
+         }
+         SubscriberAndDeviceInformation info = local.getIfPresent(id);
+         if (info != null) {
+            return info;
+         }
+         return null;
+     }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
      * org.opencord.sadis.SubscriberAndDeviceInformationService#get(java.lang.
      * String)
      */
@@ -134,7 +177,7 @@
          * from there, else check for it in the locally configured data
          */
         if (this.url == null) {
-            info = localCfgData.get(id);
+            info = (localCfgData == null) ? null : localCfgData.get(id);
 
             if (info != null) {
                 local.put(id, info);
@@ -150,13 +193,12 @@
             buf.append(id);
 
             try (InputStream io = new URL(buf.toString()).openStream()) {
-                ObjectMapper mapper = new ObjectMapper();
                 info = mapper.readValue(io, SubscriberAndDeviceInformation.class);
                 local.put(id, info);
                 return info;
             } catch (IOException e) {
                 // TODO Auto-generated catch block
-                e.printStackTrace();
+                log.warn("Exception while reading remote data " + e.getMessage());
             }
         }
         log.error("Data not found for id {}", id);
diff --git a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationCodec.java b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationCodec.java
new file mode 100644
index 0000000..18b63ac
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationCodec.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.sadis.impl;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+
+public  class SubscriberAndDeviceInformationCodec extends JsonCodec<SubscriberAndDeviceInformation> {
+      @Override
+      public ObjectNode encode(SubscriberAndDeviceInformation entry, CodecContext context) {
+          final ObjectNode result = context.mapper().createObjectNode()
+                                    .put("id", entry.id())
+                                    .put("cTag", (entry.cTag() == null) ? "" : entry.cTag().toString())
+                                    .put("sTag", (entry.sTag() == null) ? "" : entry.sTag().toString())
+                                    .put("nasPortId", entry.nasPortId())
+                                    .put("port", entry.port())
+                                    .put("slot", entry.slot())
+                                    .put("hardwareIdentifier", (entry.hardwareIdentifier() == null) ? "" :
+                                          entry.hardwareIdentifier().toString())
+                                    .put("ipAddress", (entry.ipAddress() == null) ? "" : entry.ipAddress().toString())
+                                    .put("nasId", entry.nasId());
+          return result;
+      }
+}
diff --git a/app/src/main/java/org/opencord/sadis/rest/AppWebResource.java b/app/src/main/java/org/opencord/sadis/rest/AppWebResource.java
deleted file mode 100644
index 2f13ee6..0000000
--- a/app/src/main/java/org/opencord/sadis/rest/AppWebResource.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.opencord.sadis.rest;
-
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.rest.AbstractWebResource;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-/**
- * Subscriber And Device Information Service web resource.
- */
-@Path("sadis")
-public class AppWebResource extends AbstractWebResource {
-
-    /**
-     * Get subscriber object.
-     *
-     * @param id
-     *            ID of the subscriber
-     *
-     * @return 200 OK
-     */
-    @GET
-    @Path("/subscriber/{id}")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response getSubscriber(@PathParam("id") String id) {
-        ObjectNode node = mapper().createObjectNode().put("hello", "world");
-        return ok(node).build();
-    }
-
-    /**
-     * Update subscriber object.
-     *
-     * @param id
-     *            ID of the subscriber
-     *
-     * @return 200 OK
-     */
-    @PUT
-    @Path("/subscriber/{id}")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response putSubscriber(@PathParam("id") String id) {
-        return Response.ok().build();
-    }
-
-    /**
-     * Create subscriber object.
-     *
-     * @return 201 Created
-     */
-    @POST
-    @Path("/subscriber")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response postSubscriber() {
-        try {
-            return Response.created(new URI("/subsciber/123")).build();
-        } catch (URISyntaxException e) {
-            return Response.serverError().build();
-        }
-    }
-
-    /**
-     * Delete subscriber object.
-     *
-     * @param id
-     *            ID of the subscriber
-     * @return 204 NoContent
-     */
-    @DELETE
-    @Path("/subscriber/{id}")
-    public Response deleteSubscriber(@PathParam("id") String id) {
-        return Response.noContent().build();
-    }
-}
diff --git a/app/src/main/java/org/opencord/sadis/rest/AppWebApplication.java b/app/src/main/java/org/opencord/sadis/rest/SadisWebApplication.java
similarity index 88%
rename from app/src/main/java/org/opencord/sadis/rest/AppWebApplication.java
rename to app/src/main/java/org/opencord/sadis/rest/SadisWebApplication.java
index 12ccaf4..47bda5c 100644
--- a/app/src/main/java/org/opencord/sadis/rest/AppWebApplication.java
+++ b/app/src/main/java/org/opencord/sadis/rest/SadisWebApplication.java
@@ -23,9 +23,9 @@
 /**
  * Subscriber And Device Infomration Service REST API web application.
  */
-public class AppWebApplication extends AbstractWebApplication {
+public class SadisWebApplication extends AbstractWebApplication {
     @Override
     public Set<Class<?>> getClasses() {
-        return getClasses(AppWebResource.class);
+        return getClasses(SadisWebResource.class);
     }
 }
diff --git a/app/src/main/java/org/opencord/sadis/rest/SadisWebResource.java b/app/src/main/java/org/opencord/sadis/rest/SadisWebResource.java
new file mode 100644
index 0000000..971292f
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/rest/SadisWebResource.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.sadis.rest;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.rest.AbstractWebResource;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.opencord.sadis.SubscriberAndDeviceInformationService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.onlab.util.ItemNotFoundException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Subscriber And Device Information Service web resource.
+ */
+@Path("sadisApp")
+public class SadisWebResource extends AbstractWebResource {
+    private final ObjectNode root = mapper().createObjectNode();
+    private final ArrayNode node = root.putArray("entry");
+    private static final String SUBSCRIBER_NOT_FOUND = "Subscriber not found";
+    private final SubscriberAndDeviceInformationService service = get(SubscriberAndDeviceInformationService.class);
+
+    /**
+     * Get subscriber object.
+     *
+     * @param id
+     *            ID of the subscriber
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("/subscriber/{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getSubscriber(@PathParam("id") String id) {
+        final SubscriberAndDeviceInformation entry = service.get(id);
+        if (entry == null) {
+           throw new ItemNotFoundException(SUBSCRIBER_NOT_FOUND);
+        }
+        node.add(codec(SubscriberAndDeviceInformation.class).encode(entry, this));
+        return ok(root).build();
+    }
+
+    /**
+      * Get subscriber object from the cache.
+      *
+      * @param id
+      *            ID of the subscriber
+      *
+      * @return 200 OK
+      */
+     @GET
+     @Path("/cache/subscriber/{id}")
+     @Produces(MediaType.APPLICATION_JSON)
+     public Response getSubscriberCache(@PathParam("id") String id) {
+         final SubscriberAndDeviceInformation entry = service.getfromCache(id);
+         if (entry == null) {
+            throw new ItemNotFoundException(SUBSCRIBER_NOT_FOUND);
+         }
+         node.add(codec(SubscriberAndDeviceInformation.class).encode(entry, this));
+         return ok(root).build();
+     }
+
+    /**
+     * Create subscriber object.
+     *
+     * @return 201 Created
+     */
+    @POST
+    @Path("/subscriber")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response postSubscriber() {
+        try {
+            return Response.created(new URI("/subsciber/123")).build();
+        } catch (URISyntaxException e) {
+            return Response.serverError().build();
+        }
+    }
+
+    /**
+     * Delete subscriber object.
+     *
+     * @param id
+     *            ID of the subscriber
+     * @return 204 NoContent
+     */
+    @DELETE
+    @Path("/cache/subscriber/{id}")
+    public Response deleteSubscriber(@PathParam("id") String id) {
+        service.invalidateId(id);
+        return Response.noContent().build();
+    }
+
+    /**
+      * Delete all the subscriber objects.
+      *
+      * @return 204 NoContent
+      */
+    @DELETE
+    @Path("/cache/subscriber/")
+    public Response deleteAllSubscribers() {
+        service.invalidateAll();
+        return Response.noContent().build();
+    }
+}
diff --git a/app/src/main/webapp/WEB-INF/web.xml b/app/src/main/webapp/WEB-INF/web.xml
index 051f3a1..4bd2157 100644
--- a/app/src/main/webapp/WEB-INF/web.xml
+++ b/app/src/main/webapp/WEB-INF/web.xml
@@ -45,7 +45,7 @@
         <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
         <init-param>
             <param-name>javax.ws.rs.Application</param-name>
-            <param-value>org.opencord.sadis.rest.AppWebApplication</param-value>
+            <param-value>org.opencord.sadis.rest.SadisWebApplication</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
     </servlet>
diff --git a/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java
index 9c03faf..70cfa23 100644
--- a/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java
+++ b/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java
@@ -36,6 +36,7 @@
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.ConfigApplyDelegate;
 import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.codec.impl.CodecManager;
 
 import org.opencord.sadis.SubscriberAndDeviceInformation;
 
@@ -66,6 +67,7 @@
         config.init(subject, "sadis-test", testConfig, mapper, delegate);
 
         this.sadis.cfgService = new MockNetworkConfigRegistry(config);
+        this.sadis.codecService = new CodecManager();
         this.sadis.activate();
     }