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/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"
 		}
 	]
 }