Currently, SADIS caches Subscriber records defining Subscriber Tagging requirements, authentication data and other service flow information. The OF Flow Table ID used to reference a Technology Profile for a given Subscriber needs to be able to be added to the Subscriber Record and a Reference to an Upstream and Downstream OF Meter also supported.

Change-Id: I2951d777e557c5354eb7ef0d9d8645c22b763740
diff --git a/README-NEW-DESIGN.md b/README-NEW-DESIGN.md
new file mode 100644
index 0000000..3f6b6f6
--- /dev/null
+++ b/README-NEW-DESIGN.md
@@ -0,0 +1,284 @@
+CHANGES
+-------------------
+
+- api package:
+
+    - New SadisService that introduces / encapsulates the information-based services is added.
+
+    - The oldest SubscriberAndDeviceInformationService name is changed as BaseInformationService. This
+    service includes the common methods / APIs for the information classes. The older methods still remain.
+    In order to support common methods for different information classes, this interface is made Generic.
+
+    - We assume that each information class must have an unique id. So, this field is moved to a new class 
+    that is called as BaseInformation.
+
+    - According to the previous item, SubscriberAndDeviceInformation class extends BaseInformation.
+
+    - BandwidthProfileInformation class that includes bandwidth profile specific fields such as PIR, CIR,
+    EIR is added and it also extends BaseInformation as well.
+
+    - We assume that each config class will have remote url, maximum cache size and cache ttl value. Also,
+    each local or remote config will have specific entries. So, BaseConfig abstract class is added and it 
+    must be extended by specific configuration classes.
+
+- cli package:
+
+    -  New CLI class BandwidthProfileGetCommand is added to get bandwidth profile context. The usage of the
+    cli command:
+        bandwidthProfile bandwidth-profile-id
+
+- impl package:
+
+    - BandwidthProfileCodec is added to encode the related bandwidth profile json entries.
+
+    - BandwidthProfileConfig class that extends BaseConfig is added.
+
+    - SubscriberAndDeviceInformationConfig class is changed extending BaseConfig.
+
+    - InformationAdapter class is made more Generic. It includes common methods for different information
+    classes.
+
+    - SubscriberManager and BandwidthProfileManager classes extend InformationAdapter and implements
+    specific methods.
+
+    - SadisManager is the implementation of SadisService and it can be used by different applications as
+    an OSGI service. When this service is activated, it create specific services such as SubscriberManager
+    and BandwidthProfileManager and handles their registrations.
+
+- rest package:
+
+    - New BandwidthProfileWebResource class is added to use bandwidth profiles via REST api.
+
+- test package:
+
+    - Unit test is added for BandwidthProfileManager class.
+
+TEST
+-------
+To test the related work:
+
+- Run ONOS 1.13.6 using "ok clean"
+
+- Go to 1.13.6/config directory and create cfg.json. If you want to test the remote address, then enter the entry:
+
+ ```
+    {
+       "apps":{
+          "org.opencord.sadis":{
+             "sadis":{
+                "integration":{
+                   "url":"http://localhost:8222/%s",
+                   "cache":{
+                      "enabled":true,
+                      "maxsize":20,
+                      "ttl":"PT1m"
+                   }
+                }
+             },
+            "bandwidthprofile" : {
+                "integration" : {
+                    "url" : "http://localhost:8222/%s",
+                    "cache":{
+                      "enabled":true,
+                      "maxsize":40,
+                      "ttl":"PT1m"
+              }
+          }
+         }
+       }
+     }
+    }
+```
+
+ If you test the local data, then enter an entry such like that:
+
+  ```
+  {
+     "apps":{
+        "org.opencord.sadis":{
+           "sadis":{
+              "integration":{
+                 "cache":{
+                    "enabled":true,
+                    "maxsize":50,
+                    "ttl":"PT1m"
+                 }
+              },
+              "entries":[
+                 {
+                    "id":"uni-128",
+                    "cTag":2,
+                    "sTag":2,
+                    "nasPortId":"uni-128",
+                    "technologyProfileId":64,
+                    "upstreamBandwidthProfile":"High-Speed-Internet",
+                    "downstreamBandwidthProfile":"User1-Specific"
+                 },
+                 {
+                    "id":"1d3eafb52e4e44b08818ab9ebaf7c0d4",
+                    "hardwareIdentifier":"00:1b:22:00:b1:78",
+                    "ipAddress":"192.168.1.252",
+                    "nasId":"B100-NASID"
+                 }
+              ]
+           },
+           "bandwidthprofile":{
+              "integration":{
+                 "cache":{
+                    "enabled":true,
+                    "maxsize":40,
+                    "ttl":"PT1m"
+                 }
+              },
+              "entries":[
+                 {
+                    "id":"High-Speed-Internet",
+                    "cir":200000000,
+                    "cbs":348000,
+                    "eir":10000000,
+                    "ebs":348000,
+                    "air":10000000
+                 },
+                 {
+                    "id":"User1-Specific",
+                    "cir":300000000,
+                    "cbs":348000,
+                    "eir":20000000,
+                    "ebs":348000,
+                    "air":30000000
+                 }
+              ]
+           }
+        }
+     }
+  }
+  ```
+
+- Install Cord-Config Oar - Be sure that this application ONOS dependency is also 1.13.6.
+  ```
+    onos-app localhost install ~/voltha-projects/config/target/cord-config-1.5.0-SNAPSHOT.oar
+    ```
+- Install and activate SADIS.
+  ```
+    onos-app localhost install ~/voltha-projects/sadis-new/sadis/app/target/sadis-app-2.2.0-SNAPSHOT.oar
+    onos-app localhost activate org.opencord.sadis
+   ```
+- Add the config that refers to cfg.json.
+  ```
+    onos-netcfg localhost /tmp/1.13.6/config/cfg.json
+   ```
+
+- Connect to ONOS CLI and run SADIS commands:
+  ```
+    sadis subscriber-id
+    bandwidthProfile bandwidth-profile-id
+  ```
+
+USAGE OF REMOTE SERVER
+
+- If you want to test the remote address connection, then you can use NGINX server.
+
+- Install Docker and Docker-Compose
+
+- Create the volume data
+
+```
+    sudo mkdir /var/sadis_data
+    cd sadis_data
+    sudo nano uni-2
+
+    enter the following:
+    {
+      "id": "uni-002",
+      "cTag": 2,
+      "sTag": 2,
+      "technologyProfileId" : 64,
+      "upstreamBandwidthProfile" : "High-Speed",
+      "downstreamBandwidthProfile" : "High-Speed"
+    }
+
+    sudo nano HighSpeed
+
+    enter the following:
+   {
+      "id": "High-Speed",
+      "cir": 1000000000,
+      "cbs": 384000,
+      "eir": 100000000,
+      "ebs": 384000,
+      "air": 100000000
+   }
+
+```
+
+- Create the docker volume
+
+```
+    docker volume create --name sadis_data
+    docker create --name helper --volume sadis_data:/sadis_data busybox true
+    docker cp /var/sadis_data/. helper:/sadis_data/
+    docker rm helper
+```
+
+- Clone NGINX server
+
+```
+    cd ~/voltha-projects
+    git clone https://github.com/indiehosters/nginx
+    cd nginx
+```
+
+- Change the Docker-Compose file like this
+
+```
+version: "3.0"
+services:
+    sadis:
+        image: "nginx"
+        ports:
+            - 8222:8222
+        # Move the port from 80 to 8222, on which nginx listens
+        command: /bin/bash -c "cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.orig && sed -e 's/80/8222/g' /etc/nginx/conf.d/default.conf.orig | envsubst > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
+        deploy:
+            replicas: 1
+            placement:
+                constraints:
+                    - node.role == manager
+        volumes:
+            - sadis_data:/usr/share/nginx/html:ro
+volumes:
+    sadis_data:
+        external:
+            name: sadis_data
+```
+
+- Start the NGINX docker
+
+```
+docker-compose up -d
+sudo docker ps -a (nginx status must be up)
+```
+
+- If you want to use two different NGINX docker for subscriber and bandwidth info separately,
+then you can create two different volume (one includes only subscriber files and the other includes only bandwidth profile), and after then you can change the docker-compose file
+regarding to the volume and forwarding port.
+
+EXTEND
+------
+- If you want to add a new information to SADIS:
+
+    - Add the related Information class that extends BaseInformation such as BandwidthProfileInformation
+
+    - Add the related Config class that extends BaseConfig such as BandwidthProfileConfig
+
+    - Add the related Codec class that is used to encode related JSON
+
+    - Update SadisService - add a new method for the new information
+
+    - Create Information-Based-Manager class - it must extend InformationAdapter
+
+    - Update SadisManager based on the changed API and the newly added manager
+
+    - Add CLI and REST API for the related information
+
+    - Add unit test for the new information manager class
diff --git a/api/pom.xml b/api/pom.xml
index 45a461b..95ebb04 100755
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <artifactId>sadis</artifactId>
         <groupId>org.opencord</groupId>
-        <version>2.2.0-SNAPSHOT</version>
+        <version>3.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/api/src/main/java/org/opencord/sadis/BandwidthProfileInformation.java b/api/src/main/java/org/opencord/sadis/BandwidthProfileInformation.java
new file mode 100644
index 0000000..94835b9
--- /dev/null
+++ b/api/src/main/java/org/opencord/sadis/BandwidthProfileInformation.java
@@ -0,0 +1,123 @@
+/*
+ * 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;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Objects;
+
+/**
+ * Represents bandwidth profile details such as PIR, CIR, AIR values.
+ */
+public class BandwidthProfileInformation extends BaseInformation {
+
+    @JsonProperty(value = "cir")
+    long committedInformationRate;
+
+    @JsonProperty(value = "cbs")
+    Long committedBurstSize;
+
+    @JsonProperty(value = "eir")
+    long exceededInformationRate;
+
+    @JsonProperty(value = "ebs")
+    Long exceededBurstSize;
+
+    @JsonProperty(value = "air")
+    long assuredInformationRate;
+
+    //note that: the burst size of assured bandwidth will be always 0
+    //the rate information must be in Kbps and burst must be in Kbits
+
+    protected BandwidthProfileInformation() {
+    }
+
+    public final long committedInformationRate() {
+        return this.committedInformationRate;
+    }
+
+    public final void setCommittedInformationRate(final long committedInformationRate) {
+        this.committedInformationRate = committedInformationRate;
+    }
+
+    public final Long committedBurstSize() {
+        return this.committedBurstSize;
+    }
+
+    public final void setCommittedBurstSize(final Long committedBurstSize) {
+        this.committedBurstSize = committedBurstSize;
+    }
+
+    public final long exceededInformationRate() {
+        return this.exceededInformationRate;
+    }
+
+    public final void setExceededInformationRate(final long exceededInformationRate) {
+        this.exceededInformationRate = exceededInformationRate;
+    }
+
+    public final Long exceededBurstSize() {
+        return this.exceededBurstSize;
+    }
+
+    public final void setExceededBurstSize(final Long exceededBurstSize) {
+        this.exceededBurstSize = exceededBurstSize;
+    }
+
+    public final long assuredInformationRate() {
+        return this.assuredInformationRate;
+    }
+
+    public final void setAssuredInformationRate(final long assuredInformationRate) {
+        this.assuredInformationRate = assuredInformationRate;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        BandwidthProfileInformation that = (BandwidthProfileInformation) o;
+        return committedInformationRate == that.committedInformationRate &&
+                exceededInformationRate == that.exceededInformationRate &&
+                assuredInformationRate == that.assuredInformationRate &&
+                Objects.equals(committedBurstSize, that.committedBurstSize) &&
+                Objects.equals(exceededBurstSize, that.exceededBurstSize);
+    }
+
+    @Override
+    public int hashCode() {
+
+        return Objects.hash(committedInformationRate, committedBurstSize, exceededInformationRate, exceededBurstSize,
+                assuredInformationRate);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("BandwidthProfileInformation{");
+        sb.append("id=").append(id);
+        sb.append(", committedInformationRate=").append(committedInformationRate);
+        sb.append(", committedBurstSize=").append(committedBurstSize);
+        sb.append(", exceededInformationRate=").append(exceededInformationRate);
+        sb.append(", exceededBurstSize=").append(exceededBurstSize);
+        sb.append(", assuredInformationRate=").append(assuredInformationRate);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/api/src/main/java/org/opencord/sadis/BaseConfig.java b/api/src/main/java/org/opencord/sadis/BaseConfig.java
new file mode 100644
index 0000000..75951f7
--- /dev/null
+++ b/api/src/main/java/org/opencord/sadis/BaseConfig.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.Duration;
+import java.util.List;
+
+public abstract class BaseConfig<T extends BaseInformation> extends Config<ApplicationId> {
+
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    protected static final String INTEGRATION = "integration";
+    protected static final String CACHE = "cache";
+    protected static final String CACHE_SIZE = "maxsize";
+    protected static final String CACHE_TTL = "ttl";
+    protected static final String URL = "url";
+    protected static final String ENTRIES = "entries";
+    protected static final String DEFAULT_CACHE_TTL = "PT0S";
+    protected static final String ID_SUB_PATTERN = "%s";
+
+    /**
+     * Returns Integration URL.
+     *
+     * @return configured URL or null
+     * @throws MalformedURLException specified URL not valid
+     */
+    public final URL getUrl() throws MalformedURLException {
+        final JsonNode integration = this.object.path(INTEGRATION);
+        if (integration.isMissingNode()) {
+            return null;
+        }
+
+        final JsonNode url = integration.path(URL);
+        if (url.isMissingNode()) {
+            return null;
+        }
+        StringBuilder buf = new StringBuilder(ID_SUB_PATTERN);
+        if (!url.asText().contains(buf)) {
+            log.error("Error in url, missing {}", ID_SUB_PATTERN);
+            return null;
+        }
+        return new URL(url.asText());
+    }
+
+    /**
+     * Returns Cache Maximum Size.
+     *
+     * @return configured cache max size or -1
+     */
+    public final int getCacheMaxSize() {
+        final JsonNode integration = this.object.path(INTEGRATION);
+        if (integration.isMissingNode()) {
+            return -1;
+        }
+
+        final JsonNode cache = integration.path(CACHE);
+        if (cache.isMissingNode()) {
+            return -1;
+        }
+
+        return cache.path(CACHE_SIZE).asInt(-1);
+    }
+
+    public final Duration getCacheTtl() {
+        final JsonNode integration = this.object.path(INTEGRATION);
+        if (integration.isMissingNode()) {
+            return Duration.parse(DEFAULT_CACHE_TTL);
+        }
+
+        final JsonNode cache = integration.path(CACHE);
+        if (cache.isMissingNode()) {
+            return Duration.parse(DEFAULT_CACHE_TTL);
+        }
+
+        return Duration.parse(cache.path(CACHE_TTL).asText(DEFAULT_CACHE_TTL));
+    }
+
+    public abstract List<T> getEntries();
+
+}
diff --git a/api/src/main/java/org/opencord/sadis/BaseInformation.java b/api/src/main/java/org/opencord/sadis/BaseInformation.java
new file mode 100644
index 0000000..d8d8876
--- /dev/null
+++ b/api/src/main/java/org/opencord/sadis/BaseInformation.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BaseInformation {
+
+    @JsonProperty(value = "id")
+    String id;
+
+    public final String id() {
+        return this.id;
+    }
+
+    public final void setId(final String id) {
+        this.id = id;
+    }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformationService.java b/api/src/main/java/org/opencord/sadis/BaseInformationService.java
similarity index 65%
rename from api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformationService.java
rename to api/src/main/java/org/opencord/sadis/BaseInformationService.java
index be0a42e..9d2896b 100644
--- a/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformationService.java
+++ b/api/src/main/java/org/opencord/sadis/BaseInformationService.java
@@ -15,39 +15,35 @@
  */
 package org.opencord.sadis;
 
-/**
- * Subscriber And Device Information Service.
- */
-public interface SubscriberAndDeviceInformationService {
+public interface BaseInformationService<T extends BaseInformation> {
 
     /**
      * Removes all cached entries.
      */
-    public void invalidateAll();
+    void invalidateAll();
 
     /**
-      * Removes the information associated with the given ID.
-      * @param id
-      *           key to information
-      */
-    public void invalidateId(String id);
+     * Removes the information associated with the given ID.
+     *
+     * @param id key to information
+     */
+    void invalidateId(String id);
 
     /**
      * Return the information associated with the given ID.
      *
-     * @param id
-     *            key to information
+     * @param id key to information
      * @return information associated with ID, if available, else null
      */
-    public SubscriberAndDeviceInformation get(String id);
+    T get(String id);
 
     /**
      * Return the information associated with the given ID from the cache.
      *
-     * @param id
-     *            key to information
+     * @param id key to information
      * @return information associated with ID, if available in the cache, else null
      */
-    public SubscriberAndDeviceInformation getfromCache(String id);
+    T getfromCache(String id);
+
 
 }
diff --git a/api/src/main/java/org/opencord/sadis/SadisService.java b/api/src/main/java/org/opencord/sadis/SadisService.java
new file mode 100644
index 0000000..ff98273
--- /dev/null
+++ b/api/src/main/java/org/opencord/sadis/SadisService.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/**
+ * Subscriber And Device Information Service.
+ */
+public interface SadisService {
+
+
+    /**
+     * Gets the subscriber service.
+     *
+     * @return the service related to the subscribers and access devices
+     */
+    BaseInformationService<SubscriberAndDeviceInformation> getSubscriberInfoService();
+
+    /**
+     * Gets the bandwidth profile service.
+     *
+     * @return the service related to bandwidth profiles
+     */
+    BaseInformationService<BandwidthProfileInformation> getBandwidthProfileService();
+
+}
diff --git a/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformation.java b/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformation.java
index 6b64ce7..28540a0 100644
--- a/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformation.java
+++ b/api/src/main/java/org/opencord/sadis/SubscriberAndDeviceInformation.java
@@ -24,10 +24,8 @@
 /**
  * Represents a unit of information about a subscriber or access device.
  */
-public class SubscriberAndDeviceInformation {
+public class SubscriberAndDeviceInformation extends BaseInformation {
 
-    @JsonProperty(value = "id")
-    String id;
 
     @JsonProperty(value = "sTag")
     VlanId sTag;
@@ -59,17 +57,18 @@
     @JsonProperty(value = "remoteId")
     String remoteId;
 
+    @JsonProperty(value = "technologyProfileId")
+    int technologyProfileId = -1;
+
+    @JsonProperty(value = "upstreamBandwidthProfile")
+    String upstreamBandwidthProfile;
+
+    @JsonProperty(value = "downstreamBandwidthProfile")
+    String downstreamBandwidthProfile;
+
     protected SubscriberAndDeviceInformation() {
     }
 
-    public final String id() {
-        return this.id;
-    }
-
-    public final void setId(final String id) {
-        this.id = id;
-    }
-
     public final VlanId sTag() {
         return this.sTag;
     }
@@ -150,6 +149,30 @@
         this.remoteId = remoteId;
     }
 
+    public final int technologyProfileId() {
+        return this.technologyProfileId;
+    }
+
+    public final void setTechnologyProfileId(final int technologyProfileId) {
+        this.technologyProfileId = technologyProfileId;
+    }
+
+    public final String upstreamBandwidthProfile() {
+        return this.upstreamBandwidthProfile;
+    }
+
+    public final void setUpstreamBandwidthProfile(final String upstreamBandwidthProfile) {
+        this.upstreamBandwidthProfile = upstreamBandwidthProfile;
+    }
+
+    public final String downstreamBandwidthProfile() {
+        return this.downstreamBandwidthProfile;
+    }
+
+    public final void setDownstreamBandwidthProfile(final String downstreamBandwidthProfile) {
+        this.downstreamBandwidthProfile = downstreamBandwidthProfile;
+    }
+
     /*
      * (non-Javadoc)
      *
@@ -170,6 +193,11 @@
         result = prime * result + (this.nasId == null ? 0 : this.nasId.hashCode());
         result = prime + result + (this.circuitId == null ? 0 : this.circuitId.hashCode());
         result = prime + result + (this.remoteId == null ? 0 : this.remoteId.hashCode());
+        result = prime + result + this.technologyProfileId;
+        result = prime + result +
+                (this.upstreamBandwidthProfile == null ? 0 : this.upstreamBandwidthProfile.hashCode());
+        result = prime + result +
+                (this.downstreamBandwidthProfile == null ? 0 : this.downstreamBandwidthProfile.hashCode());
         return result;
     }
 
@@ -259,6 +287,23 @@
         } else if (!this.remoteId.equals(other.remoteId)) {
             return false;
         }
+        if (this.technologyProfileId != other.technologyProfileId) {
+            return false;
+        }
+        if (this.upstreamBandwidthProfile == null) {
+            if (other.upstreamBandwidthProfile != null) {
+                return false;
+            }
+        } else if (!this.upstreamBandwidthProfile.equals(other.upstreamBandwidthProfile)) {
+            return false;
+        }
+        if (this.downstreamBandwidthProfile == null) {
+            if (other.downstreamBandwidthProfile != null) {
+                return false;
+            }
+        } else if (!this.downstreamBandwidthProfile.equals(other.downstreamBandwidthProfile)) {
+            return false;
+        }
         return true;
     }
 
@@ -293,6 +338,12 @@
         buf.append(this.circuitId);
         buf.append(",remoteId:");
         buf.append(this.remoteId);
+        buf.append(",technologyProfileId:");
+        buf.append(this.technologyProfileId);
+        buf.append(",upstreamBandwidthProfile:");
+        buf.append(this.upstreamBandwidthProfile);
+        buf.append(",downstreamBandwidthProfile:");
+        buf.append(this.downstreamBandwidthProfile);
         buf.append(']');
 
         return buf.toString();
diff --git a/app/pom.xml b/app/pom.xml
index 76d1103..b5e0e07 100755
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <groupId>org.opencord</groupId>
         <artifactId>sadis</artifactId>
-        <version>2.2.0-SNAPSHOT</version>
+        <version>3.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
@@ -31,7 +31,7 @@
     <description>Subscriber and Device Information application for CORD</description>
 
     <properties>
-        <web.context>/onos/sadis</web.context>
+        <web.context>/onos</web.context>
         <api.version>2.0.0</api.version>
         <api.title>Subscriber And Device Information REST API</api.title>
         <api.description>Subscriber And Device Information REST API</api.description>
diff --git a/app/src/main/java/org/opencord/sadis/cli/BandwidthProfileGetCommand.java b/app/src/main/java/org/opencord/sadis/cli/BandwidthProfileGetCommand.java
new file mode 100644
index 0000000..1d1f032
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/cli/BandwidthProfileGetCommand.java
@@ -0,0 +1,46 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.opencord.sadis.BandwidthProfileInformation;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.SadisService;
+
+/**
+ * Bandwidth profile information service CLI.
+ */
+@Command(scope = "onos", name = "bandwidthProfile", description = "Bandwidth profile information service CLI command")
+public class BandwidthProfileGetCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "ID", description = "bandwidthProfile ID", required = true, multiValued = false)
+    String id;
+
+    private SadisService sadisService = get(SadisService.class);
+    private BaseInformationService<BandwidthProfileInformation> service = sadisService.getBandwidthProfileService();
+
+    @Override
+    protected void execute() {
+        BandwidthProfileInformation info = service.get(id);
+        if (info != null) {
+            print(info.toString());
+        } else {
+            print("Bandwidth profile not found");
+        }
+    }
+}
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 93e9830..81130a2 100644
--- a/app/src/main/java/org/opencord/sadis/cli/SubscriberGetCommand.java
+++ b/app/src/main/java/org/opencord/sadis/cli/SubscriberGetCommand.java
@@ -18,7 +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.BaseInformationService;
+import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
 
 /**
@@ -30,11 +31,12 @@
     @Argument(index = 0, name = "ID", description = "subscriber ID", required = true, multiValued = false)
     String id;
 
-    private SubscriberAndDeviceInformationService sadisService = get(SubscriberAndDeviceInformationService.class);
+    private SadisService sadisService = get(SadisService.class);
+    private BaseInformationService<SubscriberAndDeviceInformation> service = sadisService.getSubscriberInfoService();
 
     @Override
     protected void execute() {
-        SubscriberAndDeviceInformation info = sadisService.get(id);
+        SubscriberAndDeviceInformation info = service.get(id);
         if (info != null) {
            print(info.toString());
         } else {
diff --git a/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileCodec.java b/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileCodec.java
new file mode 100644
index 0000000..8bd38f1
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileCodec.java
@@ -0,0 +1,35 @@
+/*
+ * 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.BandwidthProfileInformation;
+
+public class BandwidthProfileCodec extends JsonCodec<BandwidthProfileInformation> {
+    @Override
+    public ObjectNode encode(BandwidthProfileInformation entry, CodecContext context) {
+        return context.mapper().createObjectNode()
+                .put("id", entry.id())
+                .put("cir", (entry.committedInformationRate()))
+                .put("cbs", (entry.committedBurstSize() == null) ? "" : entry.committedBurstSize().toString())
+                .put("eir", entry.exceededInformationRate())
+                .put("ebs", (entry.exceededBurstSize() == null) ? "" : entry.exceededBurstSize().toString())
+                .put("air", entry.assuredInformationRate());
+    }
+
+}
diff --git a/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileConfig.java b/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileConfig.java
new file mode 100644
index 0000000..b899f56
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileConfig.java
@@ -0,0 +1,76 @@
+/*
+ * 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.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.opencord.sadis.BandwidthProfileInformation;
+import org.opencord.sadis.BaseConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Configuration options for the Bandwidth Profile Information Service.
+ *
+ * <pre>
+ * "bandwidthprofile" : {
+ *     "integration" : {
+ *         "url" : "http://{hostname}{:port}",
+ *         "cache" : {
+ *             "enabled"  : true or false
+ *             "maxsize"  : number of entries,
+ *             "entryttl" : duration, i.e. 10s or 1m
+ *         }
+ *     },
+ *     "entries" : [
+ *         {
+ *             "name"                        : string,
+ *             "cir"                         : long,
+ *             "cbs"                         : Long,
+ *             "eir"                         : long,
+ *             "ebs"                         : Long,
+ *             "air"                         : long,
+ *         }, ...
+ *     ]
+ * }
+ * </pre>
+ */
+public class BandwidthProfileConfig extends BaseConfig<BandwidthProfileInformation> {
+
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public List<BandwidthProfileInformation> getEntries() {
+        List<BandwidthProfileInformation> result = new ArrayList<>();
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule());
+        final JsonNode entries = this.object.path(ENTRIES);
+        entries.forEach(entry -> {
+            try {
+                result.add(mapper.readValue(entry.toString(), BandwidthProfileInformation.class));
+            } catch (IOException e) {
+                log.warn("Unable to parse configuration entry, '{}', error: {}", entry, e.getMessage());
+            }
+        });
+
+        return result;
+    }
+}
diff --git a/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileManager.java b/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileManager.java
new file mode 100644
index 0000000..820d224
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/impl/BandwidthProfileManager.java
@@ -0,0 +1,90 @@
+/*
+ * 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.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.ConfigFactory;
+import org.opencord.sadis.BandwidthProfileInformation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
+
+public class BandwidthProfileManager extends InformationAdapter<BandwidthProfileInformation, BandwidthProfileConfig> {
+
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    private ApplicationId appId;
+
+    @SuppressWarnings("rawtypes")
+    private final Set<ConfigFactory> factories = ImmutableSet
+            .of(new ConfigFactory<ApplicationId, BandwidthProfileConfig>(APP_SUBJECT_FACTORY,
+                    BandwidthProfileConfig.class, "bandwidthprofile") {
+                @Override
+                public BandwidthProfileConfig createConfig() {
+                    return new BandwidthProfileConfig();
+                }
+            });
+
+    public BandwidthProfileManager(ApplicationId appId) {
+        this.appId = appId;
+        this.registerModule();
+        this.log.info("Started");
+    }
+
+
+    @Override
+    public void registerModule() {
+        cache = CacheBuilder.newBuilder().maximumSize(maxiumCacheSize)
+                .expireAfterAccess(cacheEntryTtl, TimeUnit.SECONDS).build();
+        mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule());
+    }
+
+    @Override
+    public ApplicationId getAppId() {
+        return this.appId;
+    }
+
+    @Override
+    public Set<ConfigFactory> getConfigFactories() {
+        return factories;
+    }
+
+    @Override
+    public JsonCodec<BandwidthProfileInformation> getCodec() {
+        return new BandwidthProfileCodec();
+    }
+
+    @Override
+    public Class<BandwidthProfileInformation> getInformationClass() {
+        return BandwidthProfileInformation.class;
+    }
+
+    @Override
+    public Class<BandwidthProfileConfig> getConfigClass() {
+        return BandwidthProfileConfig.class;
+    }
+
+}
diff --git a/app/src/main/java/org/opencord/sadis/impl/InformationAdapter.java b/app/src/main/java/org/opencord/sadis/impl/InformationAdapter.java
new file mode 100644
index 0000000..5314b73
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/impl/InformationAdapter.java
@@ -0,0 +1,228 @@
+/*
+ * 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.ObjectMapper;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Maps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.opencord.sadis.BaseInformation;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.BaseInformationService;
+import java.util.Set;
+
+
+public abstract class InformationAdapter<T extends BaseInformation, K extends BaseConfig<T>>
+        implements BaseInformationService<T> {
+
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    protected static final int DEFAULT_MAXIMUM_CACHE_SIZE = 0;
+    protected static final long DEFAULT_TTL = 0;
+    protected String url;
+    protected ObjectMapper mapper;
+    protected Cache<String, T> cache;
+    protected int maxiumCacheSize = DEFAULT_MAXIMUM_CACHE_SIZE;
+    protected long cacheEntryTtl = DEFAULT_TTL;
+
+    protected Map<String, T> localCfgData = null;
+
+    public final void updateConfig(NetworkConfigRegistry cfgService) {
+        K cfg = getConfig(cfgService);
+        if (cfg == null) {
+            this.log.warn("Configuration not available");
+            return;
+        }
+        this.log.info("Cache Max Size: {}", cfg.getCacheMaxSize());
+        this.log.info("Cache TTL:      {}", cfg.getCacheTtl().getSeconds());
+        this.log.info("Entries:        {}", cfg.getEntries());
+
+        configure(cfg);
+    }
+
+    public K getConfig(NetworkConfigRegistry cfgService) {
+        return cfgService.getConfig(getAppId(), getConfigClass());
+    }
+
+    /**
+     * Configures the Adapter for data source and cache parameters.
+     *
+     * @param cfg Configuration data.
+     */
+    public void configure(K cfg) {
+
+        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);
+        }
+
+        int maximumCacheSeize = cfg.getCacheMaxSize();
+        long cacheEntryTtl = cfg.getCacheTtl().getSeconds();
+
+        // Rebuild cache if needed
+        if (isUrlChanged(url) || maximumCacheSeize != this.maxiumCacheSize ||
+                cacheEntryTtl != this.cacheEntryTtl) {
+            this.maxiumCacheSize = maximumCacheSeize;
+            this.cacheEntryTtl = cacheEntryTtl;
+            this.url = url;
+
+            Cache<String, T> newCache = CacheBuilder.newBuilder()
+                    .maximumSize(maxiumCacheSize).expireAfterAccess(cacheEntryTtl, TimeUnit.SECONDS).build();
+            Cache<String, T> oldCache = cache;
+
+            synchronized (this) {
+                cache = newCache;
+            }
+
+            oldCache.invalidateAll();
+            oldCache.cleanUp();
+        }
+    }
+
+    protected boolean isUrlChanged(String url) {
+        if (url == null && this.url == null) {
+            return false;
+        }
+        return !((url == this.url) || (url != null && url.equals(this.url)));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opencord.sadis.SadisService#clearCache()
+     */
+    @Override
+    public void invalidateAll() {
+        cache.invalidateAll();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opencord.sadis.BaseInformationService#invalidateId()
+     */
+    @Override
+    public void invalidateId(String id) {
+        cache.invalidate(id);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opencord.sadis.BaseInformationService#getfromCache(java.lang.
+     * String)
+     */
+    @Override
+    public T getfromCache(String id) {
+        Cache<String, T> local;
+        synchronized (this) {
+            local = cache;
+        }
+        T info = local.getIfPresent(id);
+        if (info != null) {
+            return info;
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opencord.sadis.BaseInformationService#get(java.lang.
+     * String)
+     */
+    @Override
+    public T get(String id) {
+        Cache<String, T> local;
+        synchronized (this) {
+            local = cache;
+        }
+
+        T info = local.getIfPresent(id);
+        if (info != null) {
+            return info;
+        }
+
+        /*
+         * 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
+         */
+        if (this.url == null) {
+            info = (localCfgData == null) ? null : localCfgData.get(id);
+
+            if (info != null) {
+                local.put(id, info);
+                return info;
+            }
+        } else {
+            // Augment URL with query parameters
+            String urlWithSubId = this.url.replaceAll("%s", id);
+            log.info("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.debug("Data not found for id {}", id);
+        return null;
+    }
+
+    public abstract void registerModule();
+
+    public abstract Set<ConfigFactory> getConfigFactories();
+
+    public abstract JsonCodec<T> getCodec();
+
+    public abstract Class<T> getInformationClass();
+
+    public abstract Class<K> getConfigClass();
+
+    public abstract ApplicationId getAppId();
+}
diff --git a/app/src/main/java/org/opencord/sadis/impl/SadisConfig.java b/app/src/main/java/org/opencord/sadis/impl/SadisConfig.java
deleted file mode 100644
index eb48fe0..0000000
--- a/app/src/main/java/org/opencord/sadis/impl/SadisConfig.java
+++ /dev/null
@@ -1,177 +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.impl;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.Config;
-import org.opencord.sadis.SubscriberAndDeviceInformation;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.ObjectCodec;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-/**
- * Configuration options for the Subscriber And Device Information Service.
- *
- * <pre>
- * "sadis" : {
- *     "integration" : {
- *         "url" : "http://{hostname}{:port}",
- *         "cache" : {
- *             "maxsize"  : number of entries,
- *             "entryttl" : duration, i.e. 10s or 1m
- *         }
- *     },
- *     "entries" : [
- *         {
- *             "id"                 : "uniqueid",
- *             "ctag"               : int,
- *             "stag"               : int,
- *             "nasportid"          : string,
- *             "port"               : int,
- *             "slot"               : int,
- *             "hardwareidentifier" : string,
- *             "ipAddress"          : string,
- *             "nasId"              : string,
- *             "circuitId"          : string,
- *             "removeId"           : string
- *         }, ...
- *     ]
- * }
- * </pre>
- */
-public class SadisConfig extends Config<ApplicationId> {
-
-    private final Logger log = LoggerFactory.getLogger(this.getClass());
-
-    private static final String SADIS_INTEGRATION = "integration";
-    private static final String SADIS_CACHE = "cache";
-    private static final String SADIS_CACHE_SIZE = "maxsize";
-    private static final String SADIS_CACHE_TTL = "ttl";
-    private static final String SADIS_URL = "url";
-    private static final String SADIS_ENTRIES = "entries";
-    private static final String DEFAULT_CACHE_TTL = "PT0S";
-    private static final String SUBSCRIBER_ID_SUB_PATTERN = "%s";
-
-    /**
-     * Returns SADIS integration URL.
-     *
-     * @return configured URL or null
-     * @throws MalformedURLException
-     *             specified URL not valid
-     */
-    public URL getUrl() throws MalformedURLException {
-        final JsonNode integration = this.object.path(SADIS_INTEGRATION);
-        if (integration.isMissingNode()) {
-            return null;
-        }
-
-        final JsonNode url = integration.path(SADIS_URL);
-        if (url.isMissingNode()) {
-            return null;
-        }
-        StringBuffer buf = new StringBuffer(SUBSCRIBER_ID_SUB_PATTERN);
-        if (!url.asText().contains(buf)) {
-            log.error("Error in url, missing {}", SUBSCRIBER_ID_SUB_PATTERN);
-            return null;
-        }
-        return new URL(url.asText());
-    }
-
-    public int getCacheMaxSize() {
-        final JsonNode integration = this.object.path(SADIS_INTEGRATION);
-        if (integration.isMissingNode()) {
-            return -1;
-        }
-
-        final JsonNode cache = integration.path(SADIS_CACHE);
-        if (cache.isMissingNode()) {
-            return -1;
-        }
-
-        return cache.path(SADIS_CACHE_SIZE).asInt(-1);
-    }
-
-    public Duration getCacheTtl() {
-        final JsonNode integration = this.object.path(SADIS_INTEGRATION);
-        if (integration.isMissingNode()) {
-            return Duration.parse(DEFAULT_CACHE_TTL);
-        }
-
-        final JsonNode cache = integration.path(SADIS_CACHE);
-        if (cache.isMissingNode()) {
-            return Duration.parse(DEFAULT_CACHE_TTL);
-        }
-
-        return Duration.parse(cache.path(SADIS_CACHE_TTL).asText(DEFAULT_CACHE_TTL));
-    }
-
-    public List<SubscriberAndDeviceInformation> getEntries() {
-        List<SubscriberAndDeviceInformation> result = new ArrayList<SubscriberAndDeviceInformation>();
-        ObjectMapper mapper = new ObjectMapper();
-        SimpleModule module = new SimpleModule();
-        module.addDeserializer(VlanId.class, new VlanIdDeserializer());
-        module.addDeserializer(Ip4Address.class, new Ip4AddressDeserializer());
-        mapper.registerModule(module);
-        final JsonNode entries = this.object.path(SADIS_ENTRIES);
-        entries.forEach(entry -> {
-            try {
-                result.add(mapper.readValue(entry.toString(), SubscriberAndDeviceInformation.class));
-            } catch (IOException e) {
-                log.warn("Unable to parse configuration entry, '{}', error: {}", entry.toString(), e.getMessage());
-            }
-        });
-
-        return result;
-    }
-
-    public class VlanIdDeserializer extends JsonDeserializer<VlanId> {
-        @Override
-        public VlanId deserialize(JsonParser jp, DeserializationContext ctxt)
-                throws IOException, JsonProcessingException {
-            ObjectCodec oc = jp.getCodec();
-            JsonNode node = oc.readTree(jp);
-            return VlanId.vlanId((short) node.asInt());
-        }
-    }
-
-    public class Ip4AddressDeserializer extends JsonDeserializer<Ip4Address> {
-        @Override
-        public Ip4Address deserialize(JsonParser jp, DeserializationContext ctxt)
-                throws IOException, JsonProcessingException {
-            ObjectCodec oc = jp.getCodec();
-            JsonNode node = oc.readTree(jp);
-            return Ip4Address.valueOf((String) node.asText());
-        }
-    }
-}
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 cd5d0b2..bcd4ae6 100644
--- a/app/src/main/java/org/opencord/sadis/impl/SadisManager.java
+++ b/app/src/main/java/org/opencord/sadis/impl/SadisManager.java
@@ -15,43 +15,36 @@
  */
 package org.opencord.sadis.impl;
 
-import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
-
-import java.util.Set;
-
-import org.apache.felix.scr.annotations.Activate;
+import com.google.common.collect.Lists;
 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.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onosproject.codec.CodecService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.config.ConfigFactory;
 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.BaseInformationService;
+import org.opencord.sadis.BandwidthProfileInformation;
+import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
 
-/**
- * Subscriber And Device Information Service application component. Component
- * that manages the integration of ONOS into a deployment providing a bridge
- * between ONOS and deployment specific information about subscribers and access
- * devices.
- */
 @Service
 @Component(immediate = true)
-public class SadisManager extends SubscriberAndDeviceInformationAdapter {
+public class SadisManager implements SadisService {
     private final Logger log = LoggerFactory.getLogger(this.getClass());
 
     private static final String SADIS_APP = "org.opencord.sadis";
-    private ApplicationId appId;
-    private final InternalConfigListener cfgListener = new InternalConfigListener();
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
@@ -62,54 +55,60 @@
     @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") {
-                @Override
-                public SadisConfig createConfig() {
-                    return new SadisConfig();
-                }
-            });
+    private final InternalConfigListener cfgListener = new InternalConfigListener();
 
-    /**
-     * Initialize the SADIS ONOS application.
-     */
+    private SubscriberManager subscriberManager;
+    private BandwidthProfileManager bandwidthProfileManager;
+
+    private List<InformationAdapter> internalServices = Lists.newArrayList();
+
     @Activate
     protected void activate() {
+        ApplicationId appId = this.coreService.registerApplication(SADIS_APP);
+        cfgService.addListener(this.cfgListener);
 
-        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();
+        subscriberManager = new SubscriberManager(appId);
+        bandwidthProfileManager = new BandwidthProfileManager(appId);
 
-        this.log.info("Started");
+        internalServices.add(subscriberManager);
+        internalServices.add(bandwidthProfileManager);
+
+        registerAdapters();
+
+        log.info("Started");
     }
 
-    /**
-     * Cleans up resources utilized by the SADIS ONOS application.
-     */
+    private void registerAdapters() {
+        internalServices.forEach(service -> {
+            registerConfigFactory(service.getConfigFactories());
+            registerCodec(service);
+            service.updateConfig(cfgService);
+        });
+    }
+
     @Deactivate
     protected void deactivate() {
         cfgService.removeListener(cfgListener);
-        this.log.info("Stopped");
+        log.info("Stopped");
     }
 
-    /**
-     * Validates the configuration and updates any operational settings that are
-     * affected by configuration changes.
-     */
-    private void updateConfig() {
-        final SadisConfig cfg = this.cfgService.getConfig(this.appId, SadisConfig.class);
-        if (cfg == null) {
-            this.log.warn("Subscriber And Device Information Service (SADIS) configuration not available");
-            return;
-        }
-        this.log.info("Cache Mac Size: {}", cfg.getCacheMaxSize());
-        this.log.info("Cache TTL:      {}", cfg.getCacheTtl().getSeconds());
-        this.log.info("Entries:        {}", cfg.getEntries());
+    private void registerConfigFactory(Set<ConfigFactory> factories) {
+        factories.forEach(cfgService::registerConfigFactory);
+    }
 
-        configure(cfg);
+    private void registerCodec(InformationAdapter service) {
+        codecService.registerCodec(service.getInformationClass(), service.getCodec());
+    }
+
+
+    @Override
+    public BaseInformationService<SubscriberAndDeviceInformation> getSubscriberInfoService() {
+        return subscriberManager;
+    }
+
+    @Override
+    public BaseInformationService<BandwidthProfileInformation> getBandwidthProfileService() {
+        return bandwidthProfileManager;
     }
 
     /**
@@ -121,11 +120,18 @@
         public void event(final NetworkConfigEvent event) {
 
             if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED
-                    || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)
-                    && event.configClass().equals(SadisConfig.class)) {
-                SadisManager.this.updateConfig();
-                SadisManager.this.log.info("Reconfigured");
+                    || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) {
+
+                internalServices.forEach(adapter -> {
+                    if (event.configClass().equals(adapter.getConfigClass())) {
+                        adapter.updateConfig(cfgService);
+                        log.info("Reconfigured");
+                    }
+                });
             }
+
+
         }
     }
 }
+
diff --git a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationAdapter.java b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationAdapter.java
deleted file mode 100644
index b5fe72e..0000000
--- a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationAdapter.java
+++ /dev/null
@@ -1,207 +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.impl;
-
-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.Ip4Address;
-import org.onlab.packet.VlanId;
-import org.opencord.sadis.SubscriberAndDeviceInformation;
-import org.opencord.sadis.SubscriberAndDeviceInformationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-
-public abstract class SubscriberAndDeviceInformationAdapter implements SubscriberAndDeviceInformationService {
-
-    private final Logger log = LoggerFactory.getLogger(this.getClass());
-
-    private static final int DEFAULT_MAXIMUM_CACHE_SIZE = 0;
-    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;
-
-    private Map<String, SubscriberAndDeviceInformation> localCfgData = null;
-
-    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);
-    }
-
-    /**
-     * Configures the Adapter for data source and cache parameters.
-     *
-     * @param cfg Configuration data.
-     */
-    public void configure(SadisConfig cfg) {
-        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);
-        }
-
-        int maximumCacheSeize = cfg.getCacheMaxSize();
-        long cacheEntryTtl = cfg.getCacheTtl().getSeconds();
-
-        // Rebuild cache if needed
-        if (isurlChanged(url) || maximumCacheSeize != this.maxiumCacheSize ||
-                cacheEntryTtl != this.cacheEntryTtl) {
-            this.maxiumCacheSize = maximumCacheSeize;
-            this.cacheEntryTtl = cacheEntryTtl;
-            this.url = url;
-
-            Cache<String, SubscriberAndDeviceInformation> newCache = CacheBuilder.newBuilder()
-                    .maximumSize(maxiumCacheSize).expireAfterAccess(cacheEntryTtl, TimeUnit.SECONDS).build();
-            Cache<String, SubscriberAndDeviceInformation> oldCache = cache;
-
-            synchronized (this) {
-                cache = newCache;
-            }
-
-            oldCache.invalidateAll();
-            oldCache.cleanUp();
-        }
-    }
-
-    private boolean isurlChanged(String url) {
-        if (url == null && this.url == null) {
-         return false;
-        }
-        return !((url == this.url) || (url != null && url.equals(this.url)));
-    }
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see
-     * org.opencord.sadis.SubscriberAndDeviceInformationService#clearCache()
-     */
-    @Override
-    public void invalidateAll() {
-        cache.invalidateAll();
-    }
-
-    /*
-     * (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)
-     */
-    @Override
-    public SubscriberAndDeviceInformation get(String id) {
-        Cache<String, SubscriberAndDeviceInformation> local;
-        synchronized (this) {
-            local = cache;
-        }
-
-        SubscriberAndDeviceInformation info = local.getIfPresent(id);
-        if (info != null) {
-            return info;
-        }
-
-        /*
-         * 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
-         */
-        if (this.url == null) {
-            info = (localCfgData == null) ? null : localCfgData.get(id);
-
-            if (info != null) {
-                local.put(id, info);
-                return info;
-            }
-        } else {
-            // Augment URL with query parameters
-            String urlWithSubId = this.url.replaceAll("%s", id);
-
-            try (InputStream io = new URL(urlWithSubId).openStream()) {
-                info = mapper.readValue(io, SubscriberAndDeviceInformation.class);
-                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.debug("Data not found for id {}", id);
-        return null;
-    }
-}
diff --git a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationCodec.java b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationCodec.java
index e5dd02b..e3f2996 100644
--- a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationCodec.java
+++ b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationCodec.java
@@ -23,7 +23,7 @@
 public  class SubscriberAndDeviceInformationCodec extends JsonCodec<SubscriberAndDeviceInformation> {
       @Override
       public ObjectNode encode(SubscriberAndDeviceInformation entry, CodecContext context) {
-          final ObjectNode result = context.mapper().createObjectNode()
+          return context.mapper().createObjectNode()
                                     .put("id", entry.id())
                                     .put("cTag", (entry.cTag() == null) ? "" : entry.cTag().toString())
                                     .put("sTag", (entry.sTag() == null) ? "" : entry.sTag().toString())
@@ -35,8 +35,12 @@
                                     .put("ipAddress", (entry.ipAddress() == null) ? "" : entry.ipAddress().toString())
                                     .put("nasId", entry.nasId())
                                     .put("circuiltId", (entry.circuitId() == null) ? "" : entry.circuitId())
-                                    .put("remoteId", (entry.remoteId() == null) ? "" : entry.remoteId());
+                                    .put("remoteId", (entry.remoteId() == null) ? "" : entry.remoteId())
+                                    .put("technologyProfileId", entry.technologyProfileId())
+                                    .put("upstreamBandwidthProfile", (entry.upstreamBandwidthProfile() == null) ?
+                                            "" : entry.upstreamBandwidthProfile())
+                                    .put("downstreamBandwidthProfile", (entry.downstreamBandwidthProfile() == null) ?
+                                            "" : entry.downstreamBandwidthProfile());
 
-          return result;
       }
 }
diff --git a/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationConfig.java b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationConfig.java
new file mode 100644
index 0000000..61e097b
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/impl/SubscriberAndDeviceInformationConfig.java
@@ -0,0 +1,113 @@
+/*
+ * 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 java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.VlanId;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * Configuration options for the Subscriber And Device Information Service.
+ *
+ * <pre>
+ * "sadis" : {
+ *     "integration" : {
+ *         "url" : "http://{hostname}{:port}",
+ *         "cache" : {
+ *             "maxsize"  : number of entries,
+ *             "entryttl" : duration, i.e. 10s or 1m
+ *         }
+ *     },
+ *     "entries" : [
+ *         {
+ *             "id"                         : "uniqueid",
+ *             "ctag"                       : int,
+ *             "stag"                       : int,
+ *             "nasportid"                  : string,
+ *             "port"                       : int,
+ *             "slot"                       : int,
+ *             "hardwareidentifier"         : string,
+ *             "ipAddress"                  : string,
+ *             "nasId"                      : string,
+ *             "circuitId"                  : string,
+ *             "removeId"                   : string,
+ *             "technologyProfileId"        : int,
+ *             "upstreamBandwidthProfile"   : string,
+ *             "downstreamBandwidthProfile" : string
+ *         }, ...
+ *     ]
+ * }
+ * </pre>
+ */
+public class SubscriberAndDeviceInformationConfig extends BaseConfig<SubscriberAndDeviceInformation> {
+
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    public List<SubscriberAndDeviceInformation> getEntries() {
+        List<SubscriberAndDeviceInformation> result = new ArrayList<>();
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule();
+        module.addDeserializer(VlanId.class, new VlanIdDeserializer());
+        module.addDeserializer(Ip4Address.class, new Ip4AddressDeserializer());
+        mapper.registerModule(module);
+        final JsonNode entries = this.object.path(ENTRIES);
+        entries.forEach(entry -> {
+            try {
+                result.add(mapper.readValue(entry.toString(), SubscriberAndDeviceInformation.class));
+            } catch (IOException e) {
+                log.warn("Unable to parse configuration entry, '{}', error: {}", entry, e.getMessage());
+            }
+        });
+
+        return result;
+    }
+
+    public class VlanIdDeserializer extends JsonDeserializer<VlanId> {
+        @Override
+        public VlanId deserialize(JsonParser jp, DeserializationContext ctxt)
+                throws IOException {
+            ObjectCodec oc = jp.getCodec();
+            JsonNode node = oc.readTree(jp);
+            return VlanId.vlanId((short) node.asInt());
+        }
+    }
+
+    public class Ip4AddressDeserializer extends JsonDeserializer<Ip4Address> {
+        @Override
+        public Ip4Address deserialize(JsonParser jp, DeserializationContext ctxt)
+                throws IOException {
+            ObjectCodec oc = jp.getCodec();
+            JsonNode node = oc.readTree(jp);
+            return Ip4Address.valueOf(node.asText());
+        }
+    }
+}
diff --git a/app/src/main/java/org/opencord/sadis/impl/SubscriberManager.java b/app/src/main/java/org/opencord/sadis/impl/SubscriberManager.java
new file mode 100644
index 0000000..b0de0c4
--- /dev/null
+++ b/app/src/main/java/org/opencord/sadis/impl/SubscriberManager.java
@@ -0,0 +1,108 @@
+/*
+ * 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 static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.google.common.cache.CacheBuilder;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.VlanId;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.ConfigFactory;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Subscriber And Device Information Service application component. Component
+ * that manages the integration of ONOS into a deployment providing a bridge
+ * between ONOS and deployment specific information about subscribers and access
+ * devices.
+ */
+
+public class SubscriberManager extends InformationAdapter<SubscriberAndDeviceInformation,
+        SubscriberAndDeviceInformationConfig> {
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    private ApplicationId appId;
+
+    @SuppressWarnings("rawtypes")
+    private final Set<ConfigFactory> factories = ImmutableSet
+            .of(new ConfigFactory<ApplicationId, SubscriberAndDeviceInformationConfig>(APP_SUBJECT_FACTORY,
+                    SubscriberAndDeviceInformationConfig.class, "sadis") {
+                @Override
+                public SubscriberAndDeviceInformationConfig createConfig() {
+                    return new SubscriberAndDeviceInformationConfig();
+                }
+            });
+
+
+    public SubscriberManager(ApplicationId appId) {
+        this.appId = appId;
+        this.registerModule();
+        this.log.info("Started");
+    }
+
+    @Override
+    public void registerModule() {
+        cache = CacheBuilder.newBuilder().maximumSize(maxiumCacheSize)
+                .expireAfterAccess(cacheEntryTtl, TimeUnit.SECONDS).build();
+        log.info("Cache is {} and this {}", cache, this);
+        mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule();
+        SubscriberAndDeviceInformationConfig config = new SubscriberAndDeviceInformationConfig();
+        SubscriberAndDeviceInformationConfig.VlanIdDeserializer vlanID = config.new VlanIdDeserializer();
+        SubscriberAndDeviceInformationConfig.Ip4AddressDeserializer ip4Address = config.new Ip4AddressDeserializer();
+        module.addDeserializer(VlanId.class, vlanID);
+        module.addDeserializer(Ip4Address.class, ip4Address);
+        mapper.registerModule(module);
+    }
+
+    @Override
+    public ApplicationId getAppId() {
+        return this.appId;
+    }
+
+    @Override
+    public Set<ConfigFactory> getConfigFactories() {
+        return factories;
+    }
+
+    @Override
+    public JsonCodec<SubscriberAndDeviceInformation> getCodec() {
+        return new SubscriberAndDeviceInformationCodec();
+    }
+
+    @Override
+    public Class<SubscriberAndDeviceInformation> getInformationClass() {
+        return SubscriberAndDeviceInformation.class;
+    }
+
+    @Override
+    public Class<SubscriberAndDeviceInformationConfig> getConfigClass() {
+        return SubscriberAndDeviceInformationConfig.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
index 971292f..160874a 100644
--- a/app/src/main/java/org/opencord/sadis/rest/SadisWebResource.java
+++ b/app/src/main/java/org/opencord/sadis/rest/SadisWebResource.java
@@ -18,7 +18,9 @@
 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.BandwidthProfileInformation;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
 import org.onlab.util.ItemNotFoundException;
 
@@ -38,12 +40,17 @@
 /**
  * Subscriber And Device Information Service web resource.
  */
-@Path("sadisApp")
+@Path("sadis")
 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);
+    private static final String BP_NOT_FOUND = "Bandwidth Profile not found";
+    private final SadisService sadisService = get(SadisService.class);
+    private final BaseInformationService<SubscriberAndDeviceInformation> subService =
+            sadisService.getSubscriberInfoService();
+    private final BaseInformationService<BandwidthProfileInformation> bpService =
+            sadisService.getBandwidthProfileService();
 
     /**
      * Get subscriber object.
@@ -57,7 +64,7 @@
     @Path("/subscriber/{id}")
     @Produces(MediaType.APPLICATION_JSON)
     public Response getSubscriber(@PathParam("id") String id) {
-        final SubscriberAndDeviceInformation entry = service.get(id);
+        final SubscriberAndDeviceInformation entry = subService.get(id);
         if (entry == null) {
            throw new ItemNotFoundException(SUBSCRIBER_NOT_FOUND);
         }
@@ -77,7 +84,7 @@
      @Path("/cache/subscriber/{id}")
      @Produces(MediaType.APPLICATION_JSON)
      public Response getSubscriberCache(@PathParam("id") String id) {
-         final SubscriberAndDeviceInformation entry = service.getfromCache(id);
+         final SubscriberAndDeviceInformation entry = subService.getfromCache(id);
          if (entry == null) {
             throw new ItemNotFoundException(SUBSCRIBER_NOT_FOUND);
          }
@@ -111,7 +118,7 @@
     @DELETE
     @Path("/cache/subscriber/{id}")
     public Response deleteSubscriber(@PathParam("id") String id) {
-        service.invalidateId(id);
+        subService.invalidateId(id);
         return Response.noContent().build();
     }
 
@@ -123,7 +130,45 @@
     @DELETE
     @Path("/cache/subscriber/")
     public Response deleteAllSubscribers() {
-        service.invalidateAll();
+        subService.invalidateAll();
+        return Response.noContent().build();
+    }
+
+    @GET
+    @Path("/bandwidthprofile/{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getBandwidthProfile(@PathParam("id") String id) {
+        final BandwidthProfileInformation entry = bpService.get(id);
+        if (entry == null) {
+            throw new ItemNotFoundException(BP_NOT_FOUND);
+        }
+        node.add(codec(BandwidthProfileInformation.class).encode(entry, this));
+        return ok(root).build();
+    }
+
+    @GET
+    @Path("/cache/bandwidthprofile/{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getBandwidthProfileCache(@PathParam("id") String id) {
+        final BandwidthProfileInformation entry = bpService.getfromCache(id);
+        if (entry == null) {
+            throw new ItemNotFoundException(BP_NOT_FOUND);
+        }
+        node.add(codec(BandwidthProfileInformation.class).encode(entry, this));
+        return ok(root).build();
+    }
+
+    @DELETE
+    @Path("/cache/bandwidthprofile/{id}")
+    public Response deleteBandwidthProfile(@PathParam("id") String id) {
+        bpService.invalidateAll();
+        return Response.noContent().build();
+    }
+
+    @DELETE
+    @Path("/cache/bandwidthprofile/")
+    public Response deleteAllBandwidthProfiles() {
+        bpService.invalidateAll();
         return Response.noContent().build();
     }
 }
diff --git a/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 9f55d62..ff8e7bc 100644
--- a/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -19,6 +19,9 @@
         <command>
             <action class="org.opencord.sadis.cli.SubscriberGetCommand"/>
         </command>
+        <command>
+            <action class="org.opencord.sadis.cli.BandwidthProfileGetCommand"/>
+        </command>
     </command-bundle>
 
 </blueprint>
diff --git a/app/src/test/java/org/opencord/sadis/impl/BandwidthProfileManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/BandwidthProfileManagerTest.java
new file mode 100644
index 0000000..9ea46c4
--- /dev/null
+++ b/app/src/test/java/org/opencord/sadis/impl/BandwidthProfileManagerTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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 org.junit.Before;
+import org.junit.Test;
+import org.opencord.sadis.BandwidthProfileInformation;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.BaseInformation;
+import org.opencord.sadis.BaseInformationService;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class BandwidthProfileManagerTest extends BaseSadis {
+
+    BandwidthProfileBuilder bp1 = BandwidthProfileBuilder.build("High Speed", 1000000000, 384000L,
+            100000000, 384000L, 100000000);
+
+    BandwidthProfileBuilder bp2 = BandwidthProfileBuilder.build("Home User Speed", 1000000000, 200000L,
+            100000000, 200000L, 100000000);
+
+    @Before
+    public void setUp() throws Exception {
+        config = new BandwidthProfileConfig();
+        super.setUp("/LocalBpConfig.json", BandwidthProfileConfig.class);
+    }
+
+    @Test
+    public void testConfiguration() {
+        BandwidthProfileConfig bpConfig = sadis.cfgService.getConfig(null, BandwidthProfileConfig.class);
+        checkConfigInfo(60, "PT1m", bpConfig);
+        checkEntriesForBandwidthProfiles(bpConfig);
+    }
+
+    @Test
+    public void testLocalMode() throws Exception {
+
+        BaseInformationService<BandwidthProfileInformation> bpService = sadis.getBandwidthProfileService();
+        checkGetForExisting("High Speed", bp1, bpService);
+        checkGetForExisting("Home User Speed", bp2, bpService);
+
+        invalidateId("High Speed", bpService);
+        checkFromBoth("High Speed", bp1, bpService);
+
+        invalidateAll(bpService);
+        checkFromBoth("Home User Speed", bp2, bpService);
+    }
+
+    @Test
+    public void testRemoteMode() throws Exception {
+        BaseInformationService<BandwidthProfileInformation> service = sadis.getBandwidthProfileService();
+        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        checkGetForExisting("HighSpeed", bp1, service);
+
+        invalidateId("HighSpeed", service);
+        checkFromBoth("HighSpeed", bp1, service);
+
+        invalidateAll(service);
+        checkFromBoth("HighSpeed", bp1, service);
+    }
+
+    private void checkEntriesForBandwidthProfiles(BaseConfig config) {
+        List<BandwidthProfileInformation> entries = config.getEntries();
+        assertEquals(2, entries.size());
+
+        BandwidthProfileInformation bpi = BandwidthProfileBuilder.build("High Speed", 1000000000, 384000L, 100000000,
+                384000L, 100000000);
+        assertTrue(checkEquality(bpi, entries.get(0)));
+
+        bpi = BandwidthProfileBuilder.build("Home User Speed", 1000000000, 200000L, 100000000,
+                200000L, 100000000);
+        assertTrue(checkEquality(bpi, entries.get(1)));
+    }
+
+    private static final class BandwidthProfileBuilder extends BandwidthProfileInformation {
+
+        public static BandwidthProfileBuilder build(String id, long cir, Long cbs, long eir, Long ebs, long air) {
+            BandwidthProfileBuilder info = new BandwidthProfileBuilder();
+            info.setId(id);
+
+            if (cbs != null) {
+                info.setCommittedBurstSize(cbs);
+            } else {
+                info.setCommittedBurstSize(0L);
+            }
+            info.setCommittedInformationRate(cir);
+
+            info.setExceededInformationRate(eir);
+            if (ebs != null) {
+                info.setExceededBurstSize(ebs);
+            } else {
+                info.setExceededBurstSize(0L);
+            }
+
+            info.setAssuredInformationRate(air);
+            return info;
+        }
+    }
+
+    @Override
+    boolean checkEquality(BaseInformation localEntry, BaseInformation entry) {
+        BandwidthProfileInformation bpi = (BandwidthProfileInformation) localEntry;
+        BandwidthProfileInformation other = (BandwidthProfileInformation) entry;
+
+        if (other == null) {
+            return false;
+        }
+
+        if (bpi.id() == null) {
+            if (other.id() != null) {
+                return false;
+            }
+        } else if (!bpi.id().equals(other.id())) {
+            return false;
+        }
+
+        if (bpi.committedInformationRate() != other.committedInformationRate()) {
+            return false;
+        }
+
+        if (!bpi.committedBurstSize().equals(other.committedBurstSize())) {
+            return false;
+        }
+
+        if (bpi.exceededInformationRate() != other.exceededInformationRate()) {
+            return false;
+        }
+
+        if (!bpi.exceededBurstSize().equals(other.exceededBurstSize())) {
+            return false;
+        }
+
+        if (bpi.assuredInformationRate() != other.assuredInformationRate()) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/app/src/test/java/org/opencord/sadis/impl/BaseSadis.java b/app/src/test/java/org/opencord/sadis/impl/BaseSadis.java
new file mode 100644
index 0000000..7d2604a
--- /dev/null
+++ b/app/src/test/java/org/opencord/sadis/impl/BaseSadis.java
@@ -0,0 +1,221 @@
+/*
+ * 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.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.BaseInformation;
+
+import java.io.InputStream;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public abstract class BaseSadis {
+
+    protected SadisManager sadis;
+    protected ConfigApplyDelegate delegate;
+    protected ObjectMapper mapper;
+    protected ApplicationId subject;
+    protected BaseConfig config;
+    protected NetworkConfigEvent event;
+    protected static NetworkConfigListener configListener;
+
+    public void setUp(String localConfig, Class configClass) throws Exception {
+        sadis = new SadisManager();
+        sadis.coreService = new MockCoreService();
+        delegate = new MockConfigDelegate();
+        mapper = new ObjectMapper();
+        subject = sadis.coreService.registerApplication("org.opencord.sadis");
+
+        config.init(subject, "sadis-local-mode-test", node(localConfig), mapper, delegate);
+        sadis.cfgService = new MockNetworkConfigRegistry(subject, config);
+
+        event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED, subject, configClass);
+
+        sadis.codecService = new CodecManager();
+        sadis.activate();
+    }
+
+    public void tearDown() {
+        this.sadis.deactivate();
+    }
+
+    protected JsonNode node(String jsonFile) throws Exception {
+        final InputStream jsonStream = BaseSadis.class.getResourceAsStream(jsonFile);
+        final JsonNode testConfig = mapper.readTree(jsonStream);
+        return testConfig;
+    }
+
+    protected void checkConfigInfo(int cacheMaxSize, String cacheTtl, BaseConfig config) {
+        assertEquals(cacheMaxSize, config.getCacheMaxSize());
+        assertEquals(Duration.parse(cacheTtl), config.getCacheTtl());
+    }
+
+    protected void invalidateId(String id, BaseInformationService service) {
+        service.invalidateId(id);
+    }
+
+    protected void invalidateAll(BaseInformationService service) {
+        service.invalidateAll();
+    }
+
+    protected void checkFromBoth(String id, BaseInformation localEntry, BaseInformationService service) {
+        BaseInformation entry = service.getfromCache(id);
+        assertNull(entry);
+        checkGetForExisting(id, localEntry, service);
+    }
+
+    abstract boolean checkEquality(BaseInformation localEntry, BaseInformation entry);
+
+    protected void checkGetForExisting(String id, BaseInformation localEntry, BaseInformationService service) {
+        BaseInformation entry = service.get(id);
+        assertNotNull(entry);
+        System.out.println(entry);
+        if (localEntry != null) {
+            assertTrue(checkEquality(localEntry, entry));
+        }
+    }
+
+
+    /**
+     * Mocks an ONOS configuration delegate to allow JSON based configuration to
+     * be tested.
+     */
+    private static final class MockConfigDelegate implements ConfigApplyDelegate {
+        @Override
+        public void onApply(@SuppressWarnings("rawtypes") Config config) {
+            config.apply();
+        }
+    }
+
+    /**
+     * Mocks an instance of {@link ApplicationId} so that the application
+     * component under test can query and use its application ID.
+     */
+    private static final class MockApplicationId implements ApplicationId {
+
+        private final short id;
+        private final String name;
+
+        public MockApplicationId(short id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        @Override
+        public short id() {
+            return id;
+        }
+
+        @Override
+        public String name() {
+            return name;
+        }
+    }
+
+    /**
+     * Mocks the core services of ONOS so that the application under test can
+     * register and query application IDs.
+     */
+    private static final class MockCoreService extends CoreServiceAdapter {
+
+        private List<ApplicationId> idList = new ArrayList<ApplicationId>();
+        private Map<String, ApplicationId> idMap = new HashMap<String, ApplicationId>();
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see
+         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.Short)
+         */
+        @Override
+        public ApplicationId getAppId(Short id) {
+            if (id >= idList.size()) {
+                return null;
+            }
+            return idList.get(id);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see
+         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.String)
+         */
+        @Override
+        public ApplicationId getAppId(String name) {
+            return idMap.get(name);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see
+         * org.onosproject.core.CoreServiceAdapter#registerApplication(java.lang
+         * .String)
+         */
+        @Override
+        public ApplicationId registerApplication(String name) {
+            ApplicationId appId = idMap.get(name);
+            if (appId == null) {
+                appId = new MockApplicationId((short) idList.size(), name);
+                idList.add(appId);
+                idMap.put(name, appId);
+            }
+            return appId;
+        }
+
+    }
+
+
+    /**
+     * Mocks the ONOS network configuration registry so that the application
+     * under test can access a JSON defined configuration.
+     */
+    static final class MockNetworkConfigRegistry<S> extends NetworkConfigRegistryAdapter {
+        private final BaseConfig config;
+
+        public MockNetworkConfigRegistry(final S subject, final BaseConfig config) {
+            this.config = config;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <S, C extends Config<S>> C getConfig(final S subject, final Class<C> configClass) {
+            return (C) config;
+        }
+
+        @Override
+        public void addListener(NetworkConfigListener listener) {
+            configListener = listener;
+        }
+    }
+}
diff --git a/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java
deleted file mode 100644
index 4111995..0000000
--- a/app/src/test/java/org/opencord/sadis/impl/SadisManagerTest.java
+++ /dev/null
@@ -1,416 +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.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.io.InputStream;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreServiceAdapter;
-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.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-
-import org.opencord.sadis.SubscriberAndDeviceInformation;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * Set of tests of the SADIS ONOS application component.
- */
-public class SadisManagerTest {
-
-    private SadisManager sadis;
-    private ObjectMapper mapper;
-    private ApplicationId subject;
-    private ConfigApplyDelegate delegate;
-    private SadisConfig config;
-    private NetworkConfigEvent event;
-    private static NetworkConfigListener configListener;
-
-     SubscriberAndDeviceInformationBuilder entry1 = SubscriberAndDeviceInformationBuilder.build("1", (short) 2,
-                  (short) 2, "1/1/2", (short) 125, (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10",
-                  "circuit123", "remote123");
-     SubscriberAndDeviceInformationBuilder entry2 = SubscriberAndDeviceInformationBuilder.build("2", (short) 4,
-                  (short) 4, "1/1/2", (short) 129, (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1",
-                  "circuit234", "remote234");
-     SubscriberAndDeviceInformationBuilder entry3 = SubscriberAndDeviceInformationBuilder.build("3", (short) 7,
-                  (short) 8, "1/1/2", (short) 130, (short) 7, "ff:aa:dd:cc:bb:ee", "MNO-NASID", "30.30.30.30",
-                  "circuit567", "remote567");
-     SubscriberAndDeviceInformationBuilder entry4 = SubscriberAndDeviceInformationBuilder.build("4", (short) 2,
-                  (short) 1, "1/1/2", (short) 132, (short) 1, "ff:cc:dd:aa:ee:bb", "PQR-NASID", "15.15.15.15",
-                  "circuit678", "remote678");
-
-    @Before
-    public void setUp() throws Exception {
-        sadis = new SadisManager();
-        sadis.coreService = new MockCoreService();
-        delegate = new MockConfigDelegate();
-        mapper = new ObjectMapper();
-        config = new SadisConfig();
-        subject = sadis.coreService.registerApplication("org.opencord.sadis");
-        config.init(subject, "sadis-local-mode-test", node("/LocalConfig.json"), mapper, delegate);
-        sadis.cfgService = new MockNetworkConfigRegistry(config);
-        event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED, subject, config.getClass());
-        sadis.codecService = new CodecManager();
-        sadis.activate();
-    }
-
-    @After
-    public void tearDown() {
-        this.sadis.deactivate();
-    }
-
-    @Test
-    public void testConfiguration() {
-        SadisConfig config = sadis.cfgService.getConfig(null, SadisConfig.class);
-        assertEquals(50, config.getCacheMaxSize());
-        assertEquals(Duration.parse("PT1m"), config.getCacheTtl());
-        List<SubscriberAndDeviceInformation> entries = config.getEntries();
-        assertEquals(3, entries.size());
-        assertTrue(SubscriberAndDeviceInformationBuilder.build("1", (short) 2, (short) 2, "1/1/2", (short) 125,
-                (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10", "circuit123", "remote123")
-                .checkEquals(entries.get(0)));
-        assertTrue(SubscriberAndDeviceInformationBuilder.build("2", (short) 4, (short) 4, "1/1/2", (short) 129,
-                (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1", "circuit234", "remote234")
-                .checkEquals(entries.get(1)));
-        assertTrue(SubscriberAndDeviceInformationBuilder.build("cc:dd:ee:ff:aa:bb", (short) -1, (short) -1, null,
-                (short) -1, (short) -1, "cc:dd:ee:ff:aa:bb", "CCC-NASID", "12.12.12.12", "circuit345", "remote345")
-                .checkEquals(entries.get(2)));
-
-    }
-
-    @Test
-    public void testLocalMode() throws Exception {
-        SubscriberAndDeviceInformation entry = sadis.get("3");
-        assertNull(entry);
-
-        entry = sadis.get("1");
-        assertNotNull(entry);
-        assertTrue(entry1.checkEquals(entry));
-
-        entry = sadis.get("2");
-        assertNotNull(entry);
-        assertTrue(entry2.checkEquals(entry));
-
-        sadis.invalidateId("1");
-        entry = sadis.getfromCache("1");
-        assertNull(entry);
-        entry = sadis.get("1");
-        assertNotNull(entry);
-        assertTrue(entry1.checkEquals(entry));
-
-        sadis.invalidateAll();
-        entry = sadis.getfromCache("2");
-        assertNull(entry);
-        entry = sadis.get("2");
-        assertNotNull(entry);
-        assertTrue(entry2.checkEquals(entry));
-    }
-
-    @Test
-    public void testRemoteMode() throws Exception {
-        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
-        configListener.event(event);
-
-        SubscriberAndDeviceInformation entry = sadis.get("3");
-        assertNotNull(entry);
-        assertTrue(entry3.checkEquals(entry));
-
-        entry = sadis.get("4");
-        assertNotNull(entry);
-        assertTrue(entry4.checkEquals(entry));
-
-        sadis.invalidateId("3");
-        entry = sadis.getfromCache("3");
-        assertNull(entry);
-        entry = sadis.get("3");
-        assertNotNull(entry);
-        assertTrue(entry3.checkEquals(entry));
-
-        sadis.invalidateAll();
-        entry = sadis.getfromCache("4");
-        assertNull(entry);
-        entry = sadis.get("4");
-        assertNotNull(entry);
-        assertTrue(entry4.checkEquals(entry));
-
-        entry = sadis.get("8");
-        assertNull(entry);
-    }
-
-    @Test
-    public void testModeSwitch() throws Exception {
-        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
-        configListener.event(event);
-        SubscriberAndDeviceInformation entry = sadis.get("3");
-        assertNotNull(entry);
-        entry = sadis.get("1");
-        assertNull(entry);
-
-        config.init(subject, "sadis-local-mode-test", node("/LocalConfig.json"), mapper, delegate);
-        configListener.event(event);
-        entry = sadis.get("1");
-        assertNotNull(entry);
-        entry = sadis.get("3");
-        assertNull(entry);
-    }
-
-    private JsonNode node(String jsonFile) throws Exception {
-        final InputStream jsonStream = SadisManagerTest.class.getResourceAsStream(jsonFile);
-        final JsonNode testConfig = mapper.readTree(jsonStream);
-        return testConfig;
-    }
-
-    // Mocks live here
-
-    private static final class SubscriberAndDeviceInformationBuilder extends SubscriberAndDeviceInformation {
-
-        public static SubscriberAndDeviceInformationBuilder build(String id, short cTag, short sTag, String nasPortId,
-                short port, short slot, String mac, String nasId, String ipAddress, String circuitId, String remoteId) {
-
-            SubscriberAndDeviceInformationBuilder info = new SubscriberAndDeviceInformationBuilder();
-            info.setId(id);
-            if (cTag != -1) {
-                info.setCTag(VlanId.vlanId(cTag));
-            }
-            if (sTag != -1) {
-                info.setSTag(VlanId.vlanId(sTag));
-            }
-            info.setNasPortId(nasPortId);
-            if (port != -1) {
-                info.setUplinkPort(port);
-            }
-            if (slot != -1) {
-                info.setSlot(slot);
-            }
-            info.setHardwareIdentifier(MacAddress.valueOf(mac));
-            info.setIPAddress(Ip4Address.valueOf(ipAddress));
-            info.setNasId(nasId);
-            info.setCircuitId(circuitId);
-            info.setRemoteId(remoteId);
-            return info;
-        }
-
-        public boolean checkEquals(SubscriberAndDeviceInformation other) {
-            if (other == null) {
-                return false;
-            }
-            if (this.cTag() == null) {
-                if (other.cTag() != null) {
-                    return false;
-                }
-            } else if (!this.cTag().equals(other.cTag())) {
-                return false;
-            }
-            if (this.hardwareIdentifier() == null) {
-                if (other.hardwareIdentifier() != null) {
-                    return false;
-                }
-            } else if (!this.hardwareIdentifier().equals(other.hardwareIdentifier())) {
-                return false;
-            }
-            if (this.id() == null) {
-                if (other.id() != null) {
-                    return false;
-                }
-            } else if (!this.id().equals(other.id())) {
-                return false;
-            }
-            if (this.nasPortId() == null) {
-                if (other.nasPortId() != null) {
-                    return false;
-                }
-            } else if (!this.nasPortId().equals(other.nasPortId())) {
-                return false;
-            }
-            if (this.nasId() == null) {
-                if (other.nasId() != null) {
-                    return false;
-                }
-            } else if (!this.nasId().equals(other.nasId())) {
-                return false;
-            }
-            if (this.ipAddress() == null) {
-                if (other.ipAddress() != null) {
-                    return false;
-                }
-            } else if (!this.ipAddress().equals(other.ipAddress())) {
-                return false;
-            }
-            if (this.uplinkPort() != other.uplinkPort()) {
-                return false;
-            }
-            if (this.sTag() == null) {
-                if (other.sTag() != null) {
-                    return false;
-                }
-            } else if (!this.sTag().equals(other.sTag())) {
-                return false;
-            }
-            if (this.slot() != other.slot()) {
-                return false;
-            }
-            if (this.circuitId() == null) {
-                if (other.circuitId() != null) {
-                    return false;
-                }
-            } else if (!this.circuitId().equals(other.circuitId())) {
-                return false;
-            }
-            if (this.remoteId() == null) {
-                if (other.remoteId() != null) {
-                    return false;
-                }
-            } else if (!this.remoteId().equals(other.remoteId())) {
-                return false;
-            }
-            return true;
-        }
-    }
-
-    /**
-     * Mocks an ONOS configuration delegate to allow JSON based configuration to
-     * be tested.
-     */
-    private static final class MockConfigDelegate implements ConfigApplyDelegate {
-        @Override
-        public void onApply(@SuppressWarnings("rawtypes") Config config) {
-            config.apply();
-        }
-    }
-
-    /**
-     * Mocks an instance of {@link ApplicationId} so that the application
-     * component under test can query and use its application ID.
-     */
-    private static final class MockApplicationId implements ApplicationId {
-
-        private final short id;
-        private final String name;
-
-        public MockApplicationId(short id, String name) {
-            this.id = id;
-            this.name = name;
-        }
-
-        @Override
-        public short id() {
-            return id;
-        }
-
-        @Override
-        public String name() {
-            return name;
-        }
-    }
-
-    /**
-     * Mocks the core services of ONOS so that the application under test can
-     * register and query application IDs.
-     */
-    private static final class MockCoreService extends CoreServiceAdapter {
-
-        private List<ApplicationId> idList = new ArrayList<ApplicationId>();
-        private Map<String, ApplicationId> idMap = new HashMap<String, ApplicationId>();
-
-        /*
-         * (non-Javadoc)
-         *
-         * @see
-         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.Short)
-         */
-        @Override
-        public ApplicationId getAppId(Short id) {
-            if (id >= idList.size()) {
-                return null;
-            }
-            return idList.get(id);
-        }
-
-        /*
-         * (non-Javadoc)
-         *
-         * @see
-         * org.onosproject.core.CoreServiceAdapter#getAppId(java.lang.String)
-         */
-        @Override
-        public ApplicationId getAppId(String name) {
-            return idMap.get(name);
-        }
-
-        /*
-         * (non-Javadoc)
-         *
-         * @see
-         * org.onosproject.core.CoreServiceAdapter#registerApplication(java.lang
-         * .String)
-         */
-        @Override
-        public ApplicationId registerApplication(String name) {
-            ApplicationId appId = idMap.get(name);
-            if (appId == null) {
-                appId = new MockApplicationId((short) idList.size(), name);
-                idList.add(appId);
-                idMap.put(name, appId);
-            }
-            return appId;
-        }
-
-    }
-
-    /**
-     * Mocks the ONOS network configuration registry so that the application
-     * under test can access a JSON defined configuration.
-     */
-    static final class MockNetworkConfigRegistry extends NetworkConfigRegistryAdapter {
-        private final SadisConfig config;
-
-        public MockNetworkConfigRegistry(final SadisConfig config) {
-            this.config = config;
-        }
-
-        @SuppressWarnings("unchecked")
-        @Override
-        public <S, C extends Config<S>> C getConfig(final S subject, final Class<C> configClass) {
-            return (C) this.config;
-        }
-
-        @Override
-        public void addListener(NetworkConfigListener listener) {
-            configListener = listener;
-        }
-    }
-}
diff --git a/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java b/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java
new file mode 100644
index 0000000..6ff5cc3
--- /dev/null
+++ b/app/src/test/java/org/opencord/sadis/impl/SubscriberAndDeviceManagerTest.java
@@ -0,0 +1,290 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.opencord.sadis.BaseConfig;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.BaseInformation;
+
+/**
+ * Set of tests of the SADIS ONOS application component.
+ */
+public class SubscriberAndDeviceManagerTest extends BaseSadis {
+
+    SubscriberAndDeviceInformationBuilder entry1 = SubscriberAndDeviceInformationBuilder.build("1", (short) 2,
+            (short) 2, "1/1/2", (short) 125, (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10",
+            "circuit123", "remote123", 64, "1Gb", "1Gb");
+    SubscriberAndDeviceInformationBuilder entry2 = SubscriberAndDeviceInformationBuilder.build("2", (short) 4,
+            (short) 4, "1/1/2", (short) 129, (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1",
+            "circuit234", "remote234", 64, "10Gb", "10Gb");
+    SubscriberAndDeviceInformationBuilder entry3 = SubscriberAndDeviceInformationBuilder.build("3", (short) 7,
+            (short) 8, "1/1/2", (short) 130, (short) 7, "ff:aa:dd:cc:bb:ee", "MNO-NASID", "30.30.30.30",
+            "circuit567", "remote567", 64, "10Gb", "10Gb");
+    SubscriberAndDeviceInformationBuilder entry4 = SubscriberAndDeviceInformationBuilder.build("4", (short) 2,
+            (short) 1, "1/1/2", (short) 132, (short) 1, "ff:cc:dd:aa:ee:bb", "PQR-NASID", "15.15.15.15",
+            "circuit678", "remote678", 64, "5Gb", "5Gb");
+
+
+    @Before
+    public void setUp() throws Exception {
+        config = new SubscriberAndDeviceInformationConfig();
+        super.setUp("/LocalSubConfig.json", SubscriberAndDeviceInformationConfig.class);
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
+    @Test
+    public void testConfiguration() {
+        SubscriberAndDeviceInformationConfig config = sadis.cfgService.getConfig(null,
+                SubscriberAndDeviceInformationConfig.class);
+        checkConfigInfo(50, "PT1m", config);
+        checkEntriesForSubscriberAndAccessDevice(config);
+    }
+
+    private void checkEntriesForSubscriberAndAccessDevice(BaseConfig config) {
+        List<SubscriberAndDeviceInformation> entries = config.getEntries();
+        assertEquals(3, entries.size());
+
+        SubscriberAndDeviceInformation sub = SubscriberAndDeviceInformationBuilder.build("1", (short) 2, (short) 2,
+                "1/1/2", (short) 125,
+                (short) 3, "aa:bb:cc:dd:ee:ff", "XXX-NASID", "10.10.10.10", "circuit123", "remote123",
+                64, "1Gb", "1Gb");
+        assertTrue(checkEquality(sub, entries.get(0)));
+
+
+        sub = SubscriberAndDeviceInformationBuilder.build("2", (short) 4, (short) 4, "1/1/2", (short) 129,
+                (short) 4, "aa:bb:cc:dd:ee:ff", "YYY-NASID", "1.1.1.1", "circuit234", "remote234",
+                64, "10Gb", "10Gb");
+        assertTrue(checkEquality(sub, entries.get(1)));
+
+        sub = SubscriberAndDeviceInformationBuilder.build("cc:dd:ee:ff:aa:bb", (short) -1, (short) -1, null,
+                (short) -1, (short) -1, "cc:dd:ee:ff:aa:bb", "CCC-NASID", "12.12.12.12", "circuit345", "remote345",
+                64, "10Gb", "10Gb");
+        assertTrue(checkEquality(sub, entries.get(2)));
+    }
+
+    @Test
+    public void testLocalMode() throws Exception {
+
+        BaseInformationService<SubscriberAndDeviceInformation> subscriberService = sadis.getSubscriberInfoService();
+
+        checkGetForExisting("1", entry1, subscriberService);
+        checkGetForExisting("2", entry2, subscriberService);
+
+        invalidateId("1", subscriberService);
+        checkFromBoth("1", entry1, subscriberService);
+
+        invalidateAll(subscriberService);
+        checkFromBoth("2", entry2, subscriberService);
+    }
+
+
+    private void checkGetForNonExist(String id, BaseInformationService service) {
+        BaseInformation entry = service.get(id);
+        assertNull(entry);
+    }
+
+    @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);
+
+        checkGetForExisting("3", entry3, subscriberService);
+        checkGetForExisting("4", entry4, subscriberService);
+
+        invalidateId("3", subscriberService);
+        checkFromBoth("3", entry3, subscriberService);
+
+        invalidateAll(subscriberService);
+        checkFromBoth("4", entry4, subscriberService);
+    }
+
+    @Test
+    public void testModeSwitch() throws Exception {
+        BaseInformationService<SubscriberAndDeviceInformation> service = sadis.getSubscriberInfoService();
+        config.init(subject, "sadis-remote-mode-test", node("/RemoteConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        checkGetForExisting("3", null, service);
+        checkGetForNonExist("1", service);
+
+        config.init(subject, "sadis-local-mode-test", node("/LocalSubConfig.json"), mapper, delegate);
+        configListener.event(event);
+
+        checkGetForExisting("1", null, service);
+        checkGetForNonExist("3", service);
+    }
+
+
+    private static final class SubscriberAndDeviceInformationBuilder extends SubscriberAndDeviceInformation {
+
+        public static SubscriberAndDeviceInformationBuilder build(String id, short cTag, short sTag, String nasPortId,
+                                                                  short port, short slot, String mac, String nasId,
+                                                                  String ipAddress, String circuitId, String remoteId,
+                                                                  int technologyProfileId,
+                                                                  String upstreamBandwidthProfile,
+                                                                  String downstreamBandwidthProfile) {
+
+            SubscriberAndDeviceInformationBuilder info = new SubscriberAndDeviceInformationBuilder();
+            info.setId(id);
+            if (cTag != -1) {
+                info.setCTag(VlanId.vlanId(cTag));
+            }
+            if (sTag != -1) {
+                info.setSTag(VlanId.vlanId(sTag));
+            }
+            info.setNasPortId(nasPortId);
+            if (port != -1) {
+                info.setUplinkPort(port);
+            }
+            if (slot != -1) {
+                info.setSlot(slot);
+            }
+            info.setHardwareIdentifier(MacAddress.valueOf(mac));
+            info.setIPAddress(Ip4Address.valueOf(ipAddress));
+            info.setNasId(nasId);
+            info.setCircuitId(circuitId);
+            info.setRemoteId(remoteId);
+
+            if (technologyProfileId != -1) {
+                info.setTechnologyProfileId(technologyProfileId);
+            }
+
+            info.setUpstreamBandwidthProfile(upstreamBandwidthProfile);
+            info.setDownstreamBandwidthProfile(downstreamBandwidthProfile);
+
+            return info;
+        }
+
+    }
+
+    @Override
+    public boolean checkEquality(BaseInformation localEntry, BaseInformation entry) {
+        SubscriberAndDeviceInformation sub = (SubscriberAndDeviceInformation) localEntry;
+        SubscriberAndDeviceInformation other = (SubscriberAndDeviceInformation) localEntry;
+
+        if (other == null) {
+            return false;
+        }
+        if (sub.cTag() == null) {
+            if (other.cTag() != null) {
+                return false;
+            }
+        } else if (!sub.cTag().equals(other.cTag())) {
+            return false;
+        }
+        if (sub.hardwareIdentifier() == null) {
+            if (other.hardwareIdentifier() != null) {
+                return false;
+            }
+        } else if (!sub.hardwareIdentifier().equals(other.hardwareIdentifier())) {
+            return false;
+        }
+        if (sub.id() == null) {
+            if (other.id() != null) {
+                return false;
+            }
+        } else if (!sub.id().equals(other.id())) {
+            return false;
+        }
+        if (sub.nasPortId() == null) {
+            if (other.nasPortId() != null) {
+                return false;
+            }
+        } else if (!sub.nasPortId().equals(other.nasPortId())) {
+            return false;
+        }
+        if (sub.nasId() == null) {
+            if (other.nasId() != null) {
+                return false;
+            }
+        } else if (!sub.nasId().equals(other.nasId())) {
+            return false;
+        }
+        if (sub.ipAddress() == null) {
+            if (other.ipAddress() != null) {
+                return false;
+            }
+        } else if (!sub.ipAddress().equals(other.ipAddress())) {
+            return false;
+        }
+        if (sub.uplinkPort() != other.uplinkPort()) {
+            return false;
+        }
+        if (sub.sTag() == null) {
+            if (other.sTag() != null) {
+                return false;
+            }
+        } else if (!sub.sTag().equals(other.sTag())) {
+            return false;
+        }
+        if (sub.slot() != other.slot()) {
+            return false;
+        }
+        if (sub.circuitId() == null) {
+            if (other.circuitId() != null) {
+                return false;
+            }
+        } else if (!sub.circuitId().equals(other.circuitId())) {
+            return false;
+        }
+        if (sub.remoteId() == null) {
+            if (other.remoteId() != null) {
+                return false;
+            }
+        } else if (!sub.remoteId().equals(other.remoteId())) {
+            return false;
+        }
+        if (sub.technologyProfileId() != other.technologyProfileId()) {
+            return false;
+        }
+        if (sub.upstreamBandwidthProfile() == null) {
+            if (other.upstreamBandwidthProfile() != null) {
+                return false;
+            }
+        } else if (!sub.upstreamBandwidthProfile().equals(other.upstreamBandwidthProfile())) {
+            return false;
+        }
+        if (sub.downstreamBandwidthProfile() == null) {
+            if (other.downstreamBandwidthProfile() != null) {
+                return false;
+            }
+        } else if (!sub.downstreamBandwidthProfile().equals(other.downstreamBandwidthProfile())) {
+            return false;
+        }
+        return true;
+    }
+
+
+}
diff --git a/app/src/test/resources/3 b/app/src/test/resources/3
index a7fead8..496bf28 100644
--- a/app/src/test/resources/3
+++ b/app/src/test/resources/3
@@ -9,5 +9,8 @@
       "ipAddress":"30.30.30.30",
       "nasId":"MNO-NASID",
       "circuitId":"circuit567",
-      "remoteId":"remote567"
+      "remoteId":"remote567",
+      "technologyProfileId":64,
+      "upstreamBandwidthProfile":"10Gb",
+      "downstreamBandwidthProfile":"10Gb"
 }
diff --git a/app/src/test/resources/4 b/app/src/test/resources/4
index 19f00f8..757e741 100644
--- a/app/src/test/resources/4
+++ b/app/src/test/resources/4
@@ -9,5 +9,8 @@
       "ipAddress":"15.15.15.15",
       "nasId":"PQR-NASID",
       "circuitId":"circuit678",
-      "remoteId":"remote678"
+      "remoteId":"remote678",
+      "technologyProfileId":64,
+      "upstreamBandwidthProfile":"5Gb",
+      "downstreamBandwidthProfile":"5Gb"
 }
diff --git a/app/src/test/resources/HighSpeed b/app/src/test/resources/HighSpeed
new file mode 100644
index 0000000..f57465f
--- /dev/null
+++ b/app/src/test/resources/HighSpeed
@@ -0,0 +1,8 @@
+{
+      "id": "High Speed",
+      "cir": 1000000000,
+      "cbs": 384000,
+      "eir": 100000000,
+      "ebs": 384000,
+      "air": 100000000
+}
\ No newline at end of file
diff --git a/app/src/test/resources/LocalBpConfig.json b/app/src/test/resources/LocalBpConfig.json
new file mode 100644
index 0000000..aee463b
--- /dev/null
+++ b/app/src/test/resources/LocalBpConfig.json
@@ -0,0 +1,26 @@
+{
+  "integration": {
+    "cache": {
+      "maxsize": 60,
+      "ttl": "PT1m"
+    }
+  },
+  "entries": [
+    {
+      "id": "High Speed",
+      "cir": 1000000000,
+      "cbs": 384000,
+      "eir": 100000000,
+      "ebs": 384000,
+      "air": 100000000
+    },
+    {
+      "id": "Home User Speed",
+      "cir": 1000000000,
+      "cbs": 200000,
+      "eir": 100000000,
+      "ebs": 200000,
+      "air": 100000000
+    }
+  ]
+}
\ No newline at end of file
diff --git a/app/src/test/resources/LocalConfig.json b/app/src/test/resources/LocalSubConfig.json
similarity index 65%
rename from app/src/test/resources/LocalConfig.json
rename to app/src/test/resources/LocalSubConfig.json
index d72de31..99c861a 100644
--- a/app/src/test/resources/LocalConfig.json
+++ b/app/src/test/resources/LocalSubConfig.json
@@ -21,7 +21,10 @@
 			"ipAddress":"10.10.10.10",
 			"nasId":"XXX-NASID",
 			"circuitId":"circuit123",
-			"remoteId":"remote123"
+			"remoteId":"remote123",
+			"technologyProfileId":64,
+			"upstreamBandwidthProfile":"1Gb",
+			"downstreamBandwidthProfile":"1Gb"
 		},
 
 		{
@@ -35,7 +38,10 @@
 			"ipAddress":"1.1.1.1",
 			"nasId":"YYY-NASID",
 			"circuitId":"circuit234",
-			"remoteId":"remote234"
+			"remoteId":"remote234",
+			"technologyProfileId":64,
+			"upstreamBandwidthProfile":"10Gb",
+			"downstreamBandwidthProfile":"10Gb"
 		},
 
 		{
@@ -44,7 +50,10 @@
 			"ipAddress":"12.12.12.12",
 			"nasId":"CCC-NASID",
 			"circuitId":"circuit345",
-                        "remoteId":"remote345"
+			"remoteId":"remote345",
+			"technologyProfileId":64,
+			"upstreamBandwidthProfile":"10Gb",
+			"downstreamBandwidthProfile":"10Gb"
 		}
 	]
 }
diff --git a/pom.xml b/pom.xml
index 004f01f..718dc2a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,18 +22,18 @@
     <parent>
         <groupId>org.onosproject</groupId>
         <artifactId>onos-dependencies</artifactId>
-        <version>1.13.1</version>
+        <version>1.13.6</version>
         <relativePath></relativePath>
     </parent>
 
     <groupId>org.opencord</groupId>
     <artifactId>sadis</artifactId>
-    <version>2.2.0-SNAPSHOT</version>
+    <version>3.0.0-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <onos.version>1.13.1</onos.version>
+        <onos.version>1.13.6</onos.version>
         <onos.app.name>org.opencord.sadis</onos.app.name>
         <onos.app.category>Utility</onos.app.category>
         <onos.app.title>Subscriber and Access Device Information Service</onos.app.title>