Notify all metric reporters when metric registry has been changed

With existing implementation, it is difficult to notify the
metric reports on metric registry changes. With this commit,
we can spontaneously notify all reporters about the metric
registry changes, so that newly added metrics can be automatically
reported to third party monitoring system.

Change-Id: I1273194553900f6bb03e2ef6bb1b54838af1da00
diff --git a/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java b/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java
index 727d1df..effb3de 100644
--- a/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java
+++ b/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java
@@ -28,6 +28,7 @@
 import org.junit.Test;
 import org.onlab.metrics.MetricsComponent;
 import org.onlab.metrics.MetricsFeature;
+import org.onlab.metrics.MetricsReporter;
 import org.onlab.metrics.MetricsService;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
@@ -220,6 +221,18 @@
         }
 
         @Override
+        public void registerReporter(MetricsReporter reporter) {
+        }
+
+        @Override
+        public void unregisterReporter(MetricsReporter reporter) {
+        }
+
+        @Override
+        public void notifyReporters() {
+        }
+
+        @Override
         public boolean removeMetric(MetricsComponent component,
                                     MetricsFeature feature, String metricName) {
             return false;
diff --git a/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/DefaultGangliaMetricsReporter.java b/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/DefaultGangliaMetricsReporter.java
new file mode 100644
index 0000000..bd49bcf
--- /dev/null
+++ b/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/DefaultGangliaMetricsReporter.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * 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.onosproject.gangliametrics;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.ganglia.GangliaReporter;
+import info.ganglia.gmetric4j.gmetric.GMetric;
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.metrics.MetricsService;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.CoreService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.concurrent.TimeUnit;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * A metric report that reports all metrics value to ganglia monitoring server.
+ */
+@Component(immediate = true)
+public class DefaultGangliaMetricsReporter implements GangliaMetricsReporter {
+    private final Logger log = getLogger(getClass());
+
+    // we will use uni-cast mode to transfer the metrics value by default
+    private static final GMetric.UDPAddressingMode GANGLIA_MODE =
+                         GMetric.UDPAddressingMode.UNICAST;
+    private static final int REPORT_PERIOD = 1;
+    private static final TimeUnit REPORT_TIME_UNIT = TimeUnit.MINUTES;
+
+    private static final String DEFAULT_ADDRESS = "localhost";
+    private static final int DEFAULT_PORT = 8649;
+    private static final int DEFAULT_TTL = 1;
+    private static final String DEFAULT_METRIC_NAMES = "default";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MetricsService metricsService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
+    @Property(name = "monitorAll", boolValue = true,
+              label = "Enable to monitor all of metrics stored in metric registry default is true")
+    protected boolean monitorAll = true;
+
+    @Property(name = "metricNames", value = DEFAULT_METRIC_NAMES,
+              label = "Names of metric to be monitored; default metric names are 'default'")
+    protected String metricNames = DEFAULT_METRIC_NAMES;
+
+    @Property(name = "address", value = DEFAULT_ADDRESS,
+              label = "IP address of ganglia monitoring server; default is localhost")
+    protected String address = DEFAULT_ADDRESS;
+
+    @Property(name = "port", intValue = DEFAULT_PORT,
+              label = "Port number of ganglia monitoring server; default is 8649")
+    protected int port = DEFAULT_PORT;
+
+    @Property(name = "ttl", intValue = DEFAULT_TTL,
+              label = "TTL value of ganglia monitoring server; default is 1")
+    protected int ttl = DEFAULT_TTL;
+
+    private GMetric ganglia;
+    private GangliaReporter gangliaReporter;
+
+    @Activate
+    public void activate() {
+        cfgService.registerProperties(getClass());
+        coreService.registerApplication("org.onosproject.metrics.reporter");
+        metricsService.registerReporter(this);
+
+        startReport();
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        cfgService.unregisterProperties(getClass(), false);
+
+        stopReport();
+        metricsService.unregisterReporter(this);
+
+        log.info("Stopped");
+    }
+
+    @Modified
+    public void modified(ComponentContext context) {
+        readComponentConfiguration(context);
+        stopReport();
+        startReport();
+    }
+
+    @Override
+    public void startReport() {
+        configGMetric();
+        gangliaReporter = buildReporter(ganglia);
+        gangliaReporter.start(REPORT_PERIOD, REPORT_TIME_UNIT);
+        log.info("Start to report metrics to ganglia server.");
+    }
+
+    @Override
+    public void stopReport() {
+        gangliaReporter.stop();
+        ganglia = null;
+        gangliaReporter = null;
+        log.info("Stop reporting metrics to ganglia server.");
+    }
+
+    @Override
+    public void restartReport() {
+        stopReport();
+        startReport();
+    }
+
+    @Override
+    public void notifyMetricsChange() {
+        gangliaReporter.stop();
+        gangliaReporter = buildReporter(ganglia);
+        gangliaReporter.start(REPORT_PERIOD, REPORT_TIME_UNIT);
+        log.info("Metric registry has been changed, apply changes.");
+    }
+
+    /**
+     * Filters the metrics to only include a set of the given metrics.
+     *
+     * @param metricRegistry original metric registry
+     * @return filtered metric registry
+     */
+    protected MetricRegistry filter(MetricRegistry metricRegistry) {
+        if (!monitorAll) {
+            final MetricRegistry filtered = new MetricRegistry();
+            metricRegistry.getNames().stream().filter(name ->
+                    containsName(name, metricNames)).forEach(name ->
+                    filtered.register(name, metricRegistry.getMetrics().get(name)));
+            return filtered;
+        } else {
+            return metricRegistry;
+        }
+    }
+
+    /**
+     * Looks up whether the metric name contains the given prefix keywords.
+     * Note that the keywords are separated with comma as delimiter
+     *
+     * @param full the original metric name that to be compared with
+     * @param prefixes the prefix keywords that are matched against with the metric name
+     * @return boolean value that denotes whether the metric name starts with the given prefix
+     */
+    protected boolean containsName(String full, String prefixes) {
+        String[] prefixArray = StringUtils.split(prefixes, ",");
+        for (String prefix : prefixArray) {
+            if (StringUtils.startsWith(full, StringUtils.trimToEmpty(prefix))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Extracts properties from the component configuration context.
+     *
+     * @param context the component context
+     */
+    private void readComponentConfiguration(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        String addressStr = Tools.get(properties, "address");
+        address = addressStr != null ? addressStr : DEFAULT_ADDRESS;
+        log.info("Configured. Ganglia server address is {}", address);
+
+        String metricNameStr = Tools.get(properties, "metricNames");
+        metricNames = metricNameStr != null ? metricNameStr : DEFAULT_METRIC_NAMES;
+        log.info("Configured. Metric name is {}", metricNames);
+
+        Integer portConfigured = Tools.getIntegerProperty(properties, "port");
+        if (portConfigured == null) {
+            port = DEFAULT_PORT;
+            log.info("Ganglia port is not configured, default value is {}", port);
+        } else {
+            port = portConfigured;
+            log.info("Configured. Ganglia port is configured to {}", port);
+        }
+
+        Integer ttlConfigured = Tools.getIntegerProperty(properties, "ttl");
+        if (ttlConfigured == null) {
+            ttl = DEFAULT_TTL;
+            log.info("Ganglia TTL is not configured, default value is {}", ttl);
+        } else {
+            ttl = ttlConfigured;
+            log.info("Configured. Ganglia TTL is configured to {}", ttl);
+        }
+
+        Boolean monitorAllEnabled = Tools.isPropertyEnabled(properties, "monitorAll");
+        if (monitorAllEnabled == null) {
+            log.info("Monitor all metrics is not configured, " +
+                     "using current value of {}", monitorAll);
+        } else {
+            monitorAll = monitorAllEnabled;
+            log.info("Configured. Monitor all metrics is {}",
+                    monitorAll ? "enabled" : "disabled");
+        }
+    }
+
+    /**
+     * Configures parameters for GMetric.
+     */
+    private void configGMetric() {
+        try {
+            ganglia = new GMetric(address, port, GANGLIA_MODE, ttl);
+        } catch (IOException e) {
+            log.error("Fail to connect to given ganglia server!");
+        }
+    }
+
+    /**
+     * Builds reporter with the given ganglia metric.
+     *
+     * @param gMetric ganglia metric
+     * @return reporter
+     */
+    private GangliaReporter buildReporter(GMetric gMetric) {
+        MetricRegistry mr = metricsService.getMetricRegistry();
+
+        return GangliaReporter.forRegistry(filter(mr))
+                .convertRatesTo(TimeUnit.SECONDS)
+                .convertDurationsTo(TimeUnit.MILLISECONDS)
+                .build(gMetric);
+    }
+}
diff --git a/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/GangliaMetricsReporter.java b/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/GangliaMetricsReporter.java
index 4b7810f..492cf38 100644
--- a/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/GangliaMetricsReporter.java
+++ b/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/GangliaMetricsReporter.java
@@ -15,208 +15,10 @@
  */
 package org.onosproject.gangliametrics;
 
-import com.codahale.metrics.MetricRegistry;
-import com.codahale.metrics.ganglia.GangliaReporter;
-import info.ganglia.gmetric4j.gmetric.GMetric;
-import org.apache.commons.lang.StringUtils;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Modified;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.metrics.MetricsService;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.osgi.service.component.ComponentContext;
-import org.slf4j.Logger;
-
-import java.io.IOException;
-import java.util.Dictionary;
-import java.util.concurrent.TimeUnit;
-
-import static org.slf4j.LoggerFactory.getLogger;
+import org.onlab.metrics.MetricsReporter;
 
 /**
- * A metric report that reports all metrics value to ganglia monitoring server.
+ * Interface of ganglia reporter that reports all metrics value to ganglia server.
  */
-@Component(immediate = true)
-public class GangliaMetricsReporter implements MetricsReporter {
-    private final Logger log = getLogger(getClass());
-
-    // we will use uni-cast mode to transfer the metrics value by default
-    private static final GMetric.UDPAddressingMode GANGLIA_MODE =
-                         GMetric.UDPAddressingMode.UNICAST;
-    private static final int REPORT_PERIOD = 1;
-    private static final TimeUnit REPORT_TIME_UNIT = TimeUnit.MINUTES;
-
-    private static final String DEFAULT_ADDRESS = "localhost";
-    private static final int DEFAULT_PORT = 8649;
-    private static final int DEFAULT_TTL = 1;
-    private static final String DEFAULT_METRIC_NAMES = "default";
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MetricsService metricsService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ComponentConfigService cfgService;
-
-    @Property(name = "monitorAll", boolValue = true,
-              label = "Enable to monitor all of metrics stored in metric registry default is true")
-    protected boolean monitorAll = true;
-
-    @Property(name = "metricNames", value = DEFAULT_METRIC_NAMES,
-              label = "Names of metric to be monitored; default metric names are 'default'")
-    protected String metricNames = DEFAULT_METRIC_NAMES;
-
-    @Property(name = "address", value = DEFAULT_ADDRESS,
-              label = "IP address of ganglia monitoring server; default is localhost")
-    protected String address = DEFAULT_ADDRESS;
-
-    @Property(name = "port", intValue = DEFAULT_PORT,
-              label = "Port number of ganglia monitoring server; default is 8649")
-    protected int port = DEFAULT_PORT;
-
-    @Property(name = "ttl", intValue = DEFAULT_TTL,
-              label = "TTL value of ganglia monitoring server; default is 1")
-    protected int ttl = DEFAULT_TTL;
-
-    private ApplicationId appId;
-    private GMetric ganglia;
-    private GangliaReporter gangliaReporter;
-
-    @Activate
-    public void activate() {
-        cfgService.registerProperties(getClass());
-        appId = coreService.registerApplication("org.onosproject.metrics.reporter");
-
-        startReport();
-        log.info("Started");
-    }
-
-    @Deactivate
-    public void deactivate() {
-        cfgService.unregisterProperties(getClass(), false);
-
-        stopReport();
-        log.info("Stopped");
-    }
-
-    @Modified
-    public void modified(ComponentContext context) {
-        readComponentConfiguration(context);
-        stopReport();
-        startReport();
-    }
-
-    @Override
-    public void startReport() {
-        try {
-            ganglia = new GMetric(address, port, GANGLIA_MODE, ttl);
-
-            MetricRegistry mr = metricsService.getMetricRegistry();
-
-            gangliaReporter = GangliaReporter.forRegistry(filter(mr))
-                    .convertRatesTo(TimeUnit.SECONDS)
-                    .convertDurationsTo(TimeUnit.MILLISECONDS)
-                    .build(ganglia);
-            gangliaReporter.start(REPORT_PERIOD, REPORT_TIME_UNIT);
-        } catch (IOException e) {
-            log.error("Fail to connect to given ganglia server!");
-        }
-    }
-
-    @Override
-    public void stopReport() {
-        gangliaReporter.stop();
-        ganglia = null;
-        gangliaReporter = null;
-    }
-
-    /**
-     * Filters the metrics to only include a set of the given metrics.
-     *
-     * @param metricRegistry original metric registry
-     * @return filtered metric registry
-     */
-    protected MetricRegistry filter(MetricRegistry metricRegistry) {
-        if (!monitorAll) {
-            final MetricRegistry filtered = new MetricRegistry();
-            metricRegistry.getNames().stream().filter(name ->
-                    containsName(name, metricNames)).forEach(name ->
-                    filtered.register(name, metricRegistry.getMetrics().get(name)));
-            return filtered;
-        } else {
-            return metricRegistry;
-        }
-    }
-
-    /**
-     * Looks up whether the metric name contains the given prefix keywords.
-     * Note that the keywords are separated with comma as delimiter
-     *
-     * @param full the original metric name that to be compared with
-     * @param prefixes the prefix keywords that are matched against with the metric name
-     * @return boolean value that denotes whether the metric name starts with the given prefix
-     */
-    protected boolean containsName(String full, String prefixes) {
-        String[] prefixArray = StringUtils.split(prefixes, ",");
-        for (String prefix : prefixArray) {
-            if (StringUtils.startsWith(full, StringUtils.trimToEmpty(prefix))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Extracts properties from the component configuration context.
-     *
-     * @param context the component context
-     */
-    private void readComponentConfiguration(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-
-        String addressStr = Tools.get(properties, "address");
-        address = addressStr != null ? addressStr : DEFAULT_ADDRESS;
-        log.info("Configured. Ganglia server address is {}", address);
-
-        String metricNameStr = Tools.get(properties, "metricNames");
-        metricNames = metricNameStr != null ? metricNameStr : DEFAULT_METRIC_NAMES;
-        log.info("Configured. Metric name is {}", metricNames);
-
-        Integer portConfigured = Tools.getIntegerProperty(properties, "port");
-        if (portConfigured == null) {
-            port = DEFAULT_PORT;
-            log.info("Ganglia port is not configured, default value is {}", port);
-        } else {
-            port = portConfigured;
-            log.info("Configured. Ganglia port is configured to {}", port);
-        }
-
-        Integer ttlConfigured = Tools.getIntegerProperty(properties, "ttl");
-        if (ttlConfigured == null) {
-            ttl = DEFAULT_TTL;
-            log.info("Ganglia TTL is not configured, default value is {}", ttl);
-        } else {
-            ttl = ttlConfigured;
-            log.info("Configured. Ganglia TTL is configured to {}", ttl);
-        }
-
-        Boolean monitorAllEnabled = Tools.isPropertyEnabled(properties, "monitorAll");
-        if (monitorAllEnabled == null) {
-            log.info("Monitor all metrics is not configured, " +
-                     "using current value of {}", monitorAll);
-        } else {
-            monitorAll = monitorAllEnabled;
-            log.info("Configured. Monitor all metrics is {}",
-                    monitorAll ? "enabled" : "disabled");
-        }
-    }
+public interface GangliaMetricsReporter extends MetricsReporter {
 }
diff --git a/apps/gangliametrics/src/test/java/org/onosproject/gangliametrics/GangliaMetricsReporterTest.java b/apps/gangliametrics/src/test/java/org/onosproject/gangliametrics/GangliaMetricsReporterTest.java
index d8dd49b..c911a8e 100644
--- a/apps/gangliametrics/src/test/java/org/onosproject/gangliametrics/GangliaMetricsReporterTest.java
+++ b/apps/gangliametrics/src/test/java/org/onosproject/gangliametrics/GangliaMetricsReporterTest.java
@@ -28,7 +28,7 @@
  */
 public class GangliaMetricsReporterTest {
 
-    private GangliaMetricsReporter gmr;
+    private DefaultGangliaMetricsReporter gmr;
 
     private static final String METRIC_NAME1 = "consistentMap.onos-app-ids.putIfAbsent";
     private static final String METRIC_NAME2 = "consistentMap.onos-hosts.entrySet";
@@ -45,7 +45,7 @@
      */
     @Before
     public void setUp() {
-        gmr = new GangliaMetricsReporter();
+        gmr = new DefaultGangliaMetricsReporter();
     }
 
     /**
diff --git a/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/DefaultGraphiteMetricsReporter.java b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/DefaultGraphiteMetricsReporter.java
new file mode 100644
index 0000000..89453a7
--- /dev/null
+++ b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/DefaultGraphiteMetricsReporter.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * 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.onosproject.graphitemetrics;
+
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.graphite.Graphite;
+import com.codahale.metrics.graphite.GraphiteReporter;
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.metrics.MetricsService;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.CoreService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.net.InetSocketAddress;
+import java.util.Dictionary;
+import java.util.concurrent.TimeUnit;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * A metric report that reports all metrics value to graphite monitoring server.
+ */
+@Component(immediate = true)
+public class DefaultGraphiteMetricsReporter implements GraphiteMetricsReporter {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final TimeUnit REPORT_TIME_UNIT = TimeUnit.MINUTES;
+    private static final int DEFAULT_REPORT_PERIOD = 1;
+
+    private static final String DEFAULT_METRIC_NAMES = "default";
+    private static final String DEFAULT_ADDRESS = "localhost";
+    private static final int DEFAULT_PORT = 2003;
+    private static final String DEFAULT_METRIC_NAME_PREFIX = "onos";
+
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MetricsService metricsService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
+    @Property(name = "monitorAll", boolValue = true,
+            label = "Enable to monitor all of metrics stored in metric registry default is true")
+    protected boolean monitorAll = true;
+
+    @Property(name = "metricNames", value = DEFAULT_METRIC_NAMES,
+            label = "Names of metric to be monitored; default metric names are 'default'")
+    protected String metricNames = DEFAULT_METRIC_NAMES;
+
+    @Property(name = "address", value = DEFAULT_ADDRESS,
+            label = "IP address of graphite monitoring server; default is localhost")
+    protected String address = DEFAULT_ADDRESS;
+
+    @Property(name = "port", intValue = DEFAULT_PORT,
+            label = "Port number of graphite monitoring server; default is 2003")
+    protected int port = DEFAULT_PORT;
+
+    @Property(name = "reportPeriod", intValue = DEFAULT_REPORT_PERIOD,
+            label = "Reporting period of graphite monitoring server; default is 1")
+    protected int reportPeriod = DEFAULT_REPORT_PERIOD;
+
+    @Property(name = "metricNamePrefix", value = DEFAULT_METRIC_NAME_PREFIX,
+            label = "Prefix of metric name for graphite back-end server; default is 'onos'")
+    protected String metricNamePrefix = DEFAULT_METRIC_NAME_PREFIX;
+
+    private Graphite graphite;
+    private GraphiteReporter graphiteReporter;
+
+    @Activate
+    public void activate() {
+        cfgService.registerProperties(getClass());
+        coreService.registerApplication("org.onosproject.graphitemetrics");
+        metricsService.registerReporter(this);
+
+        startReport();
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        cfgService.unregisterProperties(getClass(), false);
+
+        stopReport();
+        metricsService.unregisterReporter(this);
+
+        log.info("Stopped");
+    }
+
+    @Modified
+    public void modified(ComponentContext context) {
+        readComponentConfiguration(context);
+
+        // Restarts reporting
+        stopReport();
+        startReport();
+    }
+
+    public void startReport() {
+        configGraphite();
+        graphiteReporter = buildReporter(graphite);
+        graphiteReporter.start(reportPeriod, REPORT_TIME_UNIT);
+        log.info("Start to report metrics to graphite server.");
+    }
+
+    public void stopReport() {
+        graphiteReporter.stop();
+        graphite = null;
+        graphiteReporter = null;
+        log.info("Stop reporting metrics to graphite server.");
+    }
+
+    @Override
+    public void restartReport() {
+        stopReport();
+        startReport();
+    }
+
+    @Override
+    public void notifyMetricsChange() {
+        graphiteReporter.stop();
+        graphiteReporter = buildReporter(graphite);
+        graphiteReporter.start(DEFAULT_REPORT_PERIOD, REPORT_TIME_UNIT);
+        log.info("Metric registry has been changed, apply changes.");
+    }
+
+    /**
+     * Filters the metrics to only include a set of the given metrics.
+     *
+     * @param metricRegistry original metric registry
+     * @return filtered metric registry
+     */
+    protected MetricRegistry filter(MetricRegistry metricRegistry) {
+        if (!monitorAll) {
+            final MetricRegistry filtered = new MetricRegistry();
+            metricRegistry.getNames().stream().filter(name ->
+                    containsName(name, metricNames)).forEach(name ->
+                    filtered.register(name, metricRegistry.getMetrics().get(name)));
+            return filtered;
+        } else {
+            return metricRegistry;
+        }
+    }
+
+    /**
+     * Looks up whether the metric name contains the given prefix keywords.
+     * Note that the keywords are separated with comma as delimiter
+     *
+     * @param full the original metric name that to be compared with
+     * @param prefixes the prefix keywords that are matched against with the metric name
+     * @return boolean value that denotes whether the metric name starts with the given prefix
+     */
+    protected boolean containsName(String full, String prefixes) {
+        String[] prefixArray = StringUtils.split(prefixes, ",");
+        for (String prefix : prefixArray) {
+            if (StringUtils.startsWith(full, StringUtils.trimToEmpty(prefix))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Extracts properties from the component configuration context.
+     *
+     * @param context the component context
+     */
+    private void readComponentConfiguration(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        Boolean newMonitorAll = Tools.isPropertyEnabled(properties, "monitorAll");
+        if (newMonitorAll == null) {
+            log.info("Monitor all metrics is not configured, " +
+                    "using current value of {}", monitorAll);
+        } else {
+            monitorAll = newMonitorAll;
+            log.info("Configured. Monitor all metrics is {}, ",
+                    monitorAll ? "enabled" : "disabled");
+        }
+
+        String newMetricNames = Tools.get(properties, "metricNames");
+        metricNames = newMetricNames != null ? newMetricNames : DEFAULT_METRIC_NAMES;
+        log.info("Configured. Metric name is {}", metricNames);
+
+        String newAddress = Tools.get(properties, "address");
+        address = newAddress != null ? newAddress : DEFAULT_ADDRESS;
+        log.info("Configured. Graphite monitoring server address is {}", address);
+
+        Integer newPort = Tools.getIntegerProperty(properties, "port");
+        if (newPort == null) {
+            port = DEFAULT_PORT;
+            log.info("Graphite port is not configured, default value is {}", port);
+        } else {
+            port = newPort;
+            log.info("Configured. Graphite port is configured to {}", port);
+        }
+
+        Integer newReportPeriod = Tools.getIntegerProperty(properties, "reportPeriod");
+        if (newReportPeriod == null) {
+            reportPeriod = DEFAULT_REPORT_PERIOD;
+            log.info("Report period of graphite server is not configured, " +
+                    "default value is {}", reportPeriod);
+        } else {
+            reportPeriod = newReportPeriod;
+            log.info("Configured. Report period of graphite server" +
+                    " is configured to {}", reportPeriod);
+        }
+
+        String newMetricNamePrefix = Tools.get(properties, "metricNamePrefix");
+        metricNamePrefix = newMetricNamePrefix != null ?
+                newMetricNamePrefix : DEFAULT_METRIC_NAME_PREFIX;
+
+    }
+
+    /**
+     * Configures parameters for graphite config.
+     */
+    private void configGraphite() {
+        try {
+            graphite = new Graphite(new InetSocketAddress(address, port));
+        } catch (Exception e) {
+            log.error("Fail to connect to given graphite server! : " + e.getMessage());
+        }
+    }
+
+    /**
+     * Builds reporter with the given graphite config.
+     *
+     * @param graphiteCfg graphite config
+     * @return reporter
+     */
+    private GraphiteReporter buildReporter(Graphite graphiteCfg) {
+        MetricRegistry metricRegistry = metricsService.getMetricRegistry();
+        return GraphiteReporter.forRegistry(filter(metricRegistry))
+                .prefixedWith(metricNamePrefix)
+                .convertRatesTo(TimeUnit.SECONDS)
+                .convertDurationsTo(TimeUnit.MILLISECONDS)
+                .build(graphiteCfg);
+    }
+}
diff --git a/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/GraphiteMetricsReporter.java b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/GraphiteMetricsReporter.java
index 1b03794..50d2093 100644
--- a/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/GraphiteMetricsReporter.java
+++ b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/GraphiteMetricsReporter.java
@@ -15,222 +15,10 @@
  */
 package org.onosproject.graphitemetrics;
 
-
-import com.codahale.metrics.MetricRegistry;
-import com.codahale.metrics.graphite.Graphite;
-import com.codahale.metrics.graphite.GraphiteReporter;
-import org.apache.commons.lang.StringUtils;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Modified;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onlab.metrics.MetricsService;
-import org.onosproject.cfg.ComponentConfigService;
-import org.osgi.service.component.ComponentContext;
-import org.slf4j.Logger;
-import java.net.InetSocketAddress;
-import java.util.Dictionary;
-import java.util.concurrent.TimeUnit;
-
-import static org.slf4j.LoggerFactory.getLogger;
+import org.onlab.metrics.MetricsReporter;
 
 /**
- * A metric report that reports all metrics value to graphite monitoring server.
+ * Interface of graphite reporter that reports all metrics value to graphite server.
  */
-@Component(immediate = true)
-public class GraphiteMetricsReporter {
-
-    private final Logger log = getLogger(getClass());
-
-    private static final TimeUnit REPORT_TIME_UNIT = TimeUnit.MINUTES;
-    private static final int DEFAULT_REPORT_PERIOD = 1;
-
-    private static final String DEFAULT_METRIC_NAMES = "default";
-    private static final String DEFAULT_ADDRESS = "localhost";
-    private static final int DEFAULT_PORT = 2003;
-    private static final String DEFAULT_METRIC_NAME_PREFIX = "onos";
-
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MetricsService metricsService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ComponentConfigService cfgService;
-
-    @Property(name = "monitorAll", boolValue = true,
-            label = "Enable to monitor all of metrics stored in metric registry default is true")
-    protected boolean monitorAll = true;
-
-    @Property(name = "metricNames", value = DEFAULT_METRIC_NAMES,
-            label = "Names of metric to be monitored; default metric names are 'default'")
-    protected String metricNames = DEFAULT_METRIC_NAMES;
-
-    @Property(name = "address", value = DEFAULT_ADDRESS,
-            label = "IP address of graphite monitoring server; default is localhost")
-    protected String address = DEFAULT_ADDRESS;
-
-    @Property(name = "port", intValue = DEFAULT_PORT,
-            label = "Port number of graphite monitoring server; default is 2003")
-    protected int port = DEFAULT_PORT;
-
-    @Property(name = "reportPeriod", intValue = DEFAULT_REPORT_PERIOD,
-            label = "Reporting period of graphite monitoring server; default is 1")
-    protected int reportPeriod = DEFAULT_REPORT_PERIOD;
-
-    @Property(name = "metricNamePrefix", value = DEFAULT_METRIC_NAME_PREFIX,
-            label = "Prefix of metric name for graphite back-end server; default is 'onos'")
-    protected String metricNamePrefix = DEFAULT_METRIC_NAME_PREFIX;
-
-
-    private ApplicationId appId;
-    private Graphite graphite;
-    private GraphiteReporter graphiteReporter;
-
-    @Activate
-    public void activate() {
-        cfgService.registerProperties(getClass());
-        appId = coreService.registerApplication("org.onosproject.metrics.reporter");
-
-        startReport();
-        log.info("Started");
-    }
-
-    @Deactivate
-    public void deactivate() {
-        cfgService.unregisterProperties(getClass(), false);
-
-        stopReport();
-        log.info("Stopped");
-    }
-
-    @Modified
-    public void modified(ComponentContext context) {
-        readComponentConfiguration(context);
-
-        // Restarts reporting
-        stopReport();
-        startReport();
-    }
-
-    public void startReport() {
-        try {
-            graphite = new Graphite(new InetSocketAddress(address, port));
-
-            MetricRegistry metricRegistry = metricsService.getMetricRegistry();
-
-            graphiteReporter = GraphiteReporter.forRegistry(filter(metricRegistry))
-                    .prefixedWith(metricNamePrefix)
-                    .convertRatesTo(TimeUnit.SECONDS)
-                    .convertDurationsTo(TimeUnit.MILLISECONDS)
-                    .build(graphite);
-
-            graphiteReporter.start(reportPeriod, REPORT_TIME_UNIT);
-            log.info("Start to report metrics to graphite.");
-        } catch (Exception e) {
-            log.error("Fail to connect to given graphite server! : " + e.getMessage());
-        }
-    }
-
-    public void stopReport() {
-        graphiteReporter.stop();
-        graphite = null;
-        graphiteReporter = null;
-    }
-
-    /**
-     * Filters the metrics to only include a set of the given metrics.
-     *
-     * @param metricRegistry original metric registry
-     * @return filtered metric registry
-     */
-    protected MetricRegistry filter(MetricRegistry metricRegistry) {
-        if (!monitorAll) {
-            final MetricRegistry filtered = new MetricRegistry();
-            metricRegistry.getNames().stream().filter(name ->
-                    containsName(name, metricNames)).forEach(name ->
-                    filtered.register(name, metricRegistry.getMetrics().get(name)));
-            return filtered;
-        } else {
-            return metricRegistry;
-        }
-    }
-
-    /**
-     * Looks up whether the metric name contains the given prefix keywords.
-     * Note that the keywords are separated with comma as delimiter
-     *
-     * @param full the original metric name that to be compared with
-     * @param prefixes the prefix keywords that are matched against with the metric name
-     * @return boolean value that denotes whether the metric name starts with the given prefix
-     */
-    protected boolean containsName(String full, String prefixes) {
-        String[] prefixArray = StringUtils.split(prefixes, ",");
-        for (String prefix : prefixArray) {
-            if (StringUtils.startsWith(full, StringUtils.trimToEmpty(prefix))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Extracts properties from the component configuration context.
-     *
-     * @param context the component context
-     */
-    private void readComponentConfiguration(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-
-        Boolean newMonitorAll = Tools.isPropertyEnabled(properties, "monitorAll");
-        if (newMonitorAll == null) {
-            log.info("Monitor all metrics is not configured, " +
-                    "using current value of {}", monitorAll);
-        } else {
-            monitorAll = newMonitorAll;
-            log.info("Configured. Monitor all metrics is {}, ",
-                    monitorAll ? "enabled" : "disabled");
-        }
-
-        String newMetricNames = Tools.get(properties, "metricNames");
-        metricNames = newMetricNames != null ? newMetricNames : DEFAULT_METRIC_NAMES;
-        log.info("Configured. Metric name is {}", metricNames);
-
-        String newAddress = Tools.get(properties, "address");
-        address = newAddress != null ? newAddress : DEFAULT_ADDRESS;
-        log.info("Configured. Graphite monitoring server address is {}", address);
-
-        Integer newPort = Tools.getIntegerProperty(properties, "port");
-        if (newPort == null) {
-            port = DEFAULT_PORT;
-            log.info("Graphite port is not configured, default value is {}", port);
-        } else {
-            port = newPort;
-            log.info("Configured. Graphite port is configured to {}", port);
-        }
-
-        Integer newReportPeriod = Tools.getIntegerProperty(properties, "reportPeriod");
-        if (newReportPeriod == null) {
-            reportPeriod = DEFAULT_REPORT_PERIOD;
-            log.info("Report period of graphite server is not configured, " +
-                    "default value is {}", reportPeriod);
-        } else {
-            reportPeriod = newReportPeriod;
-            log.info("Configured. Report period of graphite server" +
-                    " is configured to {}", reportPeriod);
-        }
-
-        String newMetricNamePrefix = Tools.get(properties, "metricNamePrefix");
-        metricNamePrefix = newMetricNamePrefix != null ?
-                newMetricNamePrefix : DEFAULT_METRIC_NAME_PREFIX;
-
-    }
-}
\ No newline at end of file
+public interface GraphiteMetricsReporter extends MetricsReporter {
+}
diff --git a/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java b/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
index 2e22816..60c8f54 100644
--- a/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
+++ b/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
@@ -15,35 +15,20 @@
  */
 package org.onosproject.graphitemetrics;
 
-import com.codahale.metrics.Counter;
-import com.codahale.metrics.Gauge;
-import com.codahale.metrics.Histogram;
-import com.codahale.metrics.Meter;
-import com.codahale.metrics.Metric;
-import com.codahale.metrics.MetricFilter;
 import com.codahale.metrics.MetricRegistry;
-import com.codahale.metrics.Timer;
 import com.google.common.collect.ImmutableSet;
 import org.junit.Before;
 import org.junit.Test;
-import org.onlab.metrics.MetricsComponent;
-import org.onlab.metrics.MetricsFeature;
-import org.onlab.metrics.MetricsService;
-import org.onosproject.cfg.ComponentConfigAdapter;
-import org.onosproject.core.CoreServiceAdapter;
-
-import java.util.Map;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertNotNull;
 
 /**
  * Unit test for metrics reporter of graphite.
  */
 public class GraphiteMetricsReporterTest {
 
-    private GraphiteMetricsReporter gmr;
+    private DefaultGraphiteMetricsReporter gmr;
 
     private static final String METRIC_NAME1 = "consistentMap.onos-app-ids.putIfAbsent";
     private static final String METRIC_NAME2 = "consistentMap.onos-hosts.entrySet";
@@ -60,11 +45,7 @@
      */
     @Before
     public void setUp() {
-        gmr = new GraphiteMetricsReporter();
-        gmr.coreService = new CoreServiceAdapter();
-        gmr.cfgService = new ComponentConfigAdapter();
-        gmr.metricsService = new TestMetricsService();
-        gmr.activate();
+        gmr = new DefaultGraphiteMetricsReporter();
     }
 
     /**
@@ -135,116 +116,4 @@
                 .containsAll(ImmutableSet.of(METRIC_NAME1, METRIC_NAME2,
                         METRIC_NAME3, METRIC_NAME4)));
     }
-
-    /**
-     * Tests whether the methods of MetricsService receives null value parameter.
-     */
-    private class TestMetricsService implements MetricsService {
-
-        @Override
-        public MetricsComponent registerComponent(String name) {
-            assertNotNull("Component name is null.", name);
-            return null;
-        }
-
-        @Override
-        public MetricRegistry getMetricRegistry() {
-            return null;
-        }
-
-        @Override
-        public Counter createCounter(MetricsComponent component,
-                                     MetricsFeature feature, String metricName) {
-            assertNotNull("MetricsComponent is null.", component);
-            assertNotNull("MetricsFeature is null.", feature);
-            assertNotNull("Metric name is null.", metricName);
-            return null;
-        }
-
-        @Override
-        public Histogram createHistogram(MetricsComponent component,
-                                         MetricsFeature feature, String metricName) {
-            assertNotNull("MetricsComponent is null.", component);
-            assertNotNull("MetricsFeature is null.", feature);
-            assertNotNull("Metric name is null.", metricName);
-            return null;
-        }
-
-        @Override
-        public Timer createTimer(MetricsComponent component,
-                                 MetricsFeature feature, String metricName) {
-            assertNotNull("MetricsComponent is null.", component);
-            assertNotNull("MetricsFeature is null.", feature);
-            assertNotNull("Metric name is null.", metricName);
-            return null;
-        }
-
-        @Override
-        public Meter createMeter(MetricsComponent component,
-                                 MetricsFeature feature, String metricName) {
-            assertNotNull("MetricsComponent is null.", component);
-            assertNotNull("MetricsFeature is null.", feature);
-            assertNotNull("Metric name is null.", metricName);
-            return null;
-        }
-
-        @Override
-        public <T extends Metric> T registerMetric(MetricsComponent component,
-                                                   MetricsFeature feature, String metricName, T metric) {
-            assertNotNull("MetricsComponent is null.", component);
-            assertNotNull("MetricsFeature is null.", feature);
-            assertNotNull("Metric name is null.", metricName);
-            assertNotNull("Metric is null.", metric);
-            return null;
-        }
-
-        @Override
-        public boolean removeMetric(MetricsComponent component,
-                                    MetricsFeature feature, String metricName) {
-            assertNotNull("MetricsComponent is null.", component);
-            assertNotNull("MetricsFeature is null.", feature);
-            assertNotNull("Metric name is null.", metricName);
-            return false;
-        }
-
-        @Override
-        public Map<String, Timer> getTimers(MetricFilter filter) {
-            assertNotNull("MetricFilter is null.", filter);
-            return null;
-        }
-
-        @Override
-        public Map<String, Gauge> getGauges(MetricFilter filter) {
-            assertNotNull("MetricFilter is null.", filter);
-            return null;
-        }
-
-        @Override
-        public Map<String, Counter> getCounters(MetricFilter filter) {
-            assertNotNull("MetricFilter is null.", filter);
-            return null;
-        }
-
-        @Override
-        public Map<String, Meter> getMeters(MetricFilter filter) {
-            assertNotNull("MetricFilter is null.", filter);
-            return null;
-        }
-
-        @Override
-        public Map<String, Histogram> getHistograms(MetricFilter filter) {
-            assertNotNull("MetricFilter is null.", filter);
-            return null;
-        }
-
-        @Override
-        public Map<String, Metric> getMetrics() {
-            return null;
-        }
-
-        @Override
-        public void removeMatching(MetricFilter filter) {
-            assertNotNull("MetricFilter is null.", filter);
-        }
-    }
 }
diff --git a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxDbMetricsReporter.java b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxDbMetricsReporter.java
index 923e3db..7ff636c 100644
--- a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxDbMetricsReporter.java
+++ b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxDbMetricsReporter.java
@@ -93,6 +93,9 @@
     public void activate() {
         cfgService.registerProperties(getClass());
         coreService.registerApplication("org.onosproject.influxdbmetrics");
+        metricsService.registerReporter(this);
+
+        startReport();
 
         log.info("Started");
     }
@@ -101,6 +104,9 @@
     public void deactivate() {
         cfgService.unregisterProperties(getClass(), false);
 
+        stopReport();
+        metricsService.unregisterReporter(this);
+
         log.info("Stopped");
     }
 
@@ -112,20 +118,10 @@
 
     @Override
     public void startReport() {
-        try {
-            influxDbHttpSender = new InfluxDbHttpSender(DEFAULT_PROTOCOL, address,
-                    port, database, username + SEPARATOR + password, REPORT_TIME_UNIT,
-                    DEFAULT_CONN_TIMEOUT, DEFAULT_READ_TIMEOUT);
-            MetricRegistry mr = metricsService.getMetricRegistry();
-            influxDbReporter = InfluxDbReporter.forRegistry(addHostPrefix(filter(mr)))
-                    .convertRatesTo(TimeUnit.SECONDS)
-                    .convertDurationsTo(TimeUnit.MILLISECONDS)
-                    .build(influxDbHttpSender);
-            influxDbReporter.start(REPORT_PERIOD, REPORT_TIME_UNIT);
-            log.info("Start to report metrics to influxDB.");
-        } catch (Exception e) {
-            log.error("Fail to connect to given influxDB server!");
-        }
+        configSender();
+        influxDbReporter = buildReporter(influxDbHttpSender);
+        influxDbReporter.start(REPORT_PERIOD, REPORT_TIME_UNIT);
+        log.info("Start to report metrics to influxDB.");
     }
 
     @Override
@@ -143,6 +139,14 @@
     }
 
     @Override
+    public void notifyMetricsChange() {
+        influxDbReporter.stop();
+        influxDbReporter = buildReporter(influxDbHttpSender);
+        influxDbReporter.start(REPORT_PERIOD, REPORT_TIME_UNIT);
+        log.info("Metric registry has been changed, apply changes.");
+    }
+
+    @Override
     public void config(String address, int port, String database,
                        String username, String password) {
         this.address = address;
@@ -226,4 +230,31 @@
                     monitorAll ? "enabled" : "disabled");
         }
     }
+
+    /**
+     * Configures parameters for sender.
+     */
+    private void configSender() {
+        try {
+            influxDbHttpSender = new InfluxDbHttpSender(DEFAULT_PROTOCOL, address,
+                    port, database, username + SEPARATOR + password, REPORT_TIME_UNIT,
+                    DEFAULT_CONN_TIMEOUT, DEFAULT_READ_TIMEOUT);
+        } catch (Exception e) {
+            log.error("Fail to connect to given influxDB server!");
+        }
+    }
+
+    /**
+     * Builds reporter with the given sender.
+     *
+     * @param sender sender
+     * @return reporter
+     */
+    private InfluxDbReporter buildReporter(InfluxDbHttpSender sender) {
+        MetricRegistry mr = metricsService.getMetricRegistry();
+        return InfluxDbReporter.forRegistry(addHostPrefix(filter(mr)))
+                .convertRatesTo(TimeUnit.SECONDS)
+                .convertDurationsTo(TimeUnit.MILLISECONDS)
+                .build(sender);
+    }
 }
diff --git a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsConfig.java b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsConfig.java
index 7c879b9..8b79c71 100644
--- a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsConfig.java
+++ b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsConfig.java
@@ -86,8 +86,6 @@
         coreService.registerApplication("org.onosproject.influxdbmetrics");
 
         configReporter(influxDbMetricsReporter);
-        influxDbMetricsReporter.startReport();
-
         configRetriever(influxDbMetricsRetriever);
 
         log.info("Started");
@@ -97,7 +95,6 @@
     public void deactivate() {
         cfgService.unregisterProperties(getClass(), false);
 
-        influxDbMetricsReporter.stopReport();
         log.info("Stopped");
     }
 
diff --git a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporter.java b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporter.java
index 1645ee9..e0090ee 100644
--- a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporter.java
+++ b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporter.java
@@ -15,25 +15,12 @@
  */
 package org.onosproject.influxdbmetrics;
 
+import org.onlab.metrics.MetricsReporter;
+
 /**
  * A Metric reporter interface for reporting all metrics to influxDB server.
  */
-public interface InfluxDbMetricsReporter {
-
-    /**
-     * Starts to report metrics to InfluxDB server.
-     */
-    void startReport();
-
-    /**
-     * Stops reporting metrics.
-     */
-    void stopReport();
-
-    /**
-     * Restarts metrics reporter.
-     */
-    void restartReport();
+public interface InfluxDbMetricsReporter extends MetricsReporter {
 
     /**
      * Configures default parameters for influx database metrics reporter.
diff --git a/apps/influxdbmetrics/src/test/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporterTest.java b/apps/influxdbmetrics/src/test/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporterTest.java
index de107cf..c71f0de 100644
--- a/apps/influxdbmetrics/src/test/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporterTest.java
+++ b/apps/influxdbmetrics/src/test/java/org/onosproject/influxdbmetrics/InfluxDbMetricsReporterTest.java
@@ -26,6 +26,7 @@
 import org.junit.Before;
 import org.onlab.metrics.MetricsComponent;
 import org.onlab.metrics.MetricsFeature;
+import org.onlab.metrics.MetricsReporter;
 import org.onlab.metrics.MetricsService;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.cluster.ClusterServiceAdapter;
@@ -104,6 +105,18 @@
         }
 
         @Override
+        public void registerReporter(MetricsReporter reporter) {
+        }
+
+        @Override
+        public void unregisterReporter(MetricsReporter reporter) {
+        }
+
+        @Override
+        public void notifyReporters() {
+        }
+
+        @Override
         public boolean removeMetric(MetricsComponent component,
                                     MetricsFeature feature, String metricName) {
             return false;
diff --git a/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java b/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java
index ac92a6f..d641bdc 100644
--- a/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java
+++ b/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java
@@ -86,6 +86,7 @@
 
         this.deviceId = deviceId;
         this.providerService = providerService;
+        metricsService.notifyReporters();
     }
 
     /**
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
index e8644cc..433311f 100644
--- a/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsManager.java
@@ -15,10 +15,6 @@
  */
 package org.onlab.metrics;
 
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
 import com.codahale.metrics.Counter;
 import com.codahale.metrics.Gauge;
 import com.codahale.metrics.Histogram;
@@ -27,6 +23,12 @@
 import com.codahale.metrics.MetricFilter;
 import com.codahale.metrics.MetricRegistry;
 import com.codahale.metrics.Timer;
+import com.google.common.collect.Sets;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 /**
  * This class holds the Metrics registry for ONOS.
@@ -74,6 +76,11 @@
     private MetricRegistry metricsRegistry = new MetricRegistry();
 
     /**
+     * Reporter for exposing metrics objects to third party persistent system.
+     */
+    private Set<MetricsReporter> reporters = Sets.newConcurrentHashSet();
+
+    /**
      * Clears the internal state.
      */
     protected void clear() {
@@ -216,6 +223,34 @@
     }
 
     /**
+     * Registers a reporter to receive any changes on metric registry.
+     *
+     * @param reporter metric reporter
+     */
+    @Override
+    public void registerReporter(MetricsReporter reporter) {
+        reporters.add(reporter);
+    }
+
+    /**
+     * Unregisters the given metric reporter.
+     *
+     * @param reporter metric reporter
+     */
+    @Override
+    public void unregisterReporter(MetricsReporter reporter) {
+        reporters.remove(reporter);
+    }
+
+    /**
+     * Notifies the changes on metric registry to all registered reporters.
+     */
+    @Override
+    public void notifyReporters() {
+        reporters.forEach(MetricsReporter::notifyMetricsChange);
+    }
+
+    /**
      * Removes the metric with the given name.
      *
      * @param component component the Metric is defined in
diff --git a/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/MetricsReporter.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsReporter.java
similarity index 74%
rename from apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/MetricsReporter.java
rename to utils/misc/src/main/java/org/onlab/metrics/MetricsReporter.java
index 544df32..68088a5 100644
--- a/apps/gangliametrics/src/main/java/org/onosproject/gangliametrics/MetricsReporter.java
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsReporter.java
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.gangliametrics;
+package org.onlab.metrics;
 
 /**
- * A metric report that reports all metrics value to third party monitor.
+ * Interface of reporter that reports all metrics value to third party monitor.
  */
 public interface MetricsReporter {
 
@@ -29,4 +29,14 @@
      * Stops reporting metrics.
      */
     void stopReport();
+
+    /**
+     * Restarts metrics reporter.
+     */
+    void restartReport();
+
+    /**
+     * Notifies the changes on metric registry.
+     */
+    void notifyMetricsChange();
 }
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java
index e2e223e..f207803 100644
--- a/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsService.java
@@ -113,6 +113,25 @@
              T metric);
 
     /**
+     * Registers a reporter to receive any changes on metric registry.
+     *
+     * @param reporter metric reporter
+     */
+    void registerReporter(MetricsReporter reporter);
+
+    /**
+     * Unregisters the given metric reporter.
+     *
+     * @param reporter metric reporter
+     */
+    void unregisterReporter(MetricsReporter reporter);
+
+    /**
+     * Notifies the changes on metric registry to all registered reporters.
+     */
+    void notifyReporters();
+
+    /**
      * Removes the metric with the given name.
      *
      * @param component component the Metric is defined in
diff --git a/utils/misc/src/test/java/org/onlab/metrics/MetricsServiceAdapter.java b/utils/misc/src/test/java/org/onlab/metrics/MetricsServiceAdapter.java
index d92fc4b..bdaea63 100644
--- a/utils/misc/src/test/java/org/onlab/metrics/MetricsServiceAdapter.java
+++ b/utils/misc/src/test/java/org/onlab/metrics/MetricsServiceAdapter.java
@@ -75,6 +75,18 @@
     }
 
     @Override
+    public void registerReporter(MetricsReporter reporter) {
+    }
+
+    @Override
+    public void unregisterReporter(MetricsReporter reporter) {
+    }
+
+    @Override
+    public void notifyReporters() {
+    }
+
+    @Override
     public boolean removeMetric(MetricsComponent component,
                                 MetricsFeature feature, String metricName) {
         return false;