[ONOS-4128] Backup metrics of MetricsService to Graphite monitoring server
- Separate individual app
- Initial implementation of metric service reporter
- Add unit test

Change-Id: I8f89b4dba2f6b7f311f147b776faa7d5160098e0
diff --git a/apps/graphitemetrics/features.xml b/apps/graphitemetrics/features.xml
new file mode 100644
index 0000000..c989be4
--- /dev/null
+++ b/apps/graphitemetrics/features.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ 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.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+    <feature name="${project.artifactId}" version="${project.version}"
+             description="${project.description}">
+        <feature>onos-api</feature>
+        <bundle>mvn:${project.groupId}/onos-app-graphite/${project.version}</bundle>
+        <bundle>mvn:io.dropwizard.metrics/metrics-core/3.1.2</bundle>
+        <bundle>mvn:io.dropwizard.metrics/metrics-graphite/3.1.2</bundle>
+    </feature>
+</features>
diff --git a/apps/graphitemetrics/pom.xml b/apps/graphitemetrics/pom.xml
new file mode 100644
index 0000000..349b733
--- /dev/null
+++ b/apps/graphitemetrics/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.6.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-graphite</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Performance metric service reporter and retriever for graphite</description>
+
+    <properties>
+        <onos.app.name>org.onosproject.graphitemetrics</onos.app.name>
+        <onos.app.title>Graphite Report and Query App</onos.app.title>
+        <onos.app.category>Monitoring</onos.app.category>
+        <onos.app.url>http://onosproject.org</onos.app.url>
+        <metrics.version>3.1.2</metrics.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.dropwizard.metrics</groupId>
+            <artifactId>metrics-core</artifactId>
+            <version>${metrics.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.dropwizard.metrics</groupId>
+            <artifactId>metrics-graphite</artifactId>
+            <version>${metrics.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/GraphiteMetricsReporter.java b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/GraphiteMetricsReporter.java
new file mode 100644
index 0000000..46ddd15
--- /dev/null
+++ b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/GraphiteMetricsReporter.java
@@ -0,0 +1,236 @@
+/*
+ * 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.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;
+
+/**
+ * A metric report that reports all metrics value to graphite monitoring 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
diff --git a/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/package-info.java b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/package-info.java
new file mode 100644
index 0000000..8cc8191
--- /dev/null
+++ b/apps/graphitemetrics/src/main/java/org/onosproject/graphitemetrics/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Application that provides capability to report metrics to and from
+ * graphite monitoring server.
+ */
+package org.onosproject.graphitemetrics;
\ No newline at end of file
diff --git a/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java b/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
new file mode 100644
index 0000000..91c5952
--- /dev/null
+++ b/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.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 static final String METRIC_NAME1 = "consistentMap.onos-app-ids.putIfAbsent";
+    private static final String METRIC_NAME2 = "consistentMap.onos-hosts.entrySet";
+    private static final String METRIC_NAME3 = "clusterCommunication.endpoint.*";
+    private static final String METRIC_NAME4 = "atomicCounter.onos-app-id-counter.*";
+
+    private static final String PREFIXES1 = "consistentMap";
+    private static final String PREFIXES2 = "topology";
+    private static final String PREFIXES3 = "consistentMap.onos-app-ids";
+    private static final String PREFIXES4 = "consistentMap, clusterCommunication, atomicCounter";
+
+    /**
+     * Sets up graphite metrics reporter instance.
+     */
+    @Before
+    public void setUp() {
+        gmr = new GraphiteMetricsReporter();
+        gmr.coreService = new CoreServiceAdapter();
+        gmr.cfgService = new ComponentConfigAdapter();
+        gmr.metricsService = new TestMetricsService();
+        gmr.activate();
+    }
+
+    /**
+     * Tears down graphite metrics reporter instance.
+     */
+    public void tearDown() {
+        gmr.deactivate();
+    }
+
+    /**
+     * Tests whether the containsName method can always return the correct result
+     * with the given metric name and a set of prefixes.
+     */
+    @Test
+    public void testContainsName() {
+        assertTrue(gmr.containsName(METRIC_NAME1, PREFIXES1));
+        assertTrue(gmr.containsName(METRIC_NAME1, PREFIXES3));
+        assertTrue(gmr.containsName(METRIC_NAME1, PREFIXES4));
+        assertTrue(gmr.containsName(METRIC_NAME2, PREFIXES4));
+        assertTrue(gmr.containsName(METRIC_NAME3, PREFIXES4));
+        assertTrue(gmr.containsName(METRIC_NAME4, PREFIXES4));
+        assertFalse(gmr.containsName(METRIC_NAME1, PREFIXES2));
+    }
+
+    /**
+     * Tests whether the filter method can always return the correct result.
+     */
+    @Test
+    public void testFilter() {
+        MetricRegistry filtered;
+        MetricRegistry full = new MetricRegistry();
+        full.meter(METRIC_NAME1);
+        full.meter(METRIC_NAME2);
+        full.meter(METRIC_NAME3);
+        full.meter(METRIC_NAME4);
+
+        gmr.monitorAll = true;
+        filtered = gmr.filter(full);
+
+        assertTrue(filtered.getNames()
+                .containsAll(ImmutableSet.of(METRIC_NAME1, METRIC_NAME2,
+                                             METRIC_NAME3, METRIC_NAME4)));
+
+        gmr.monitorAll = false;
+        gmr.metricNames = PREFIXES1;
+        filtered = gmr.filter(full);
+
+        assertTrue(filtered.getNames()
+                .containsAll(ImmutableSet.of(METRIC_NAME1, METRIC_NAME2)));
+        assertFalse(filtered.getNames()
+                .containsAll(ImmutableSet.of(METRIC_NAME3, METRIC_NAME4)));
+
+        gmr.metricNames = PREFIXES2;
+        filtered = gmr.filter(full);
+
+        assertFalse(filtered.getNames().containsAll(ImmutableSet.of(METRIC_NAME1)));
+
+        gmr.metricNames = PREFIXES3;
+        filtered = gmr.filter(full);
+
+        assertTrue(filtered.getNames().containsAll(ImmutableSet.of(METRIC_NAME1)));
+        assertFalse(filtered.getNames().containsAll(ImmutableSet.of(METRIC_NAME2)));
+
+        gmr.metricNames = PREFIXES4;
+        filtered = gmr.filter(full);
+
+        assertTrue(filtered.getNames()
+                .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/pom.xml b/apps/pom.xml
index 4799f9d..e271d44 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -70,6 +70,7 @@
         <module>openstackinterface</module>
         <module>influxdbmetrics</module>
         <module>gangliametrics</module>
+        <module>graphitemetrics</module>
     </modules>
 
     <properties>