Added a framework for metrics

Added a metrics framework based on the codahale Metrics
package.  ONOS can create a registry and maintain Metrics,
as well as dump out metrics values via a REST API.  Unit
tests are include that test the REST APIs.  Currently
supports Timers, Counters, Gauges, Meters, and Histograms.

Change-Id: I0f6ed87f889dc7037caf9aefc92e663702c6dda8
diff --git a/conf/onos.properties b/conf/onos.properties
index c8509cf..a19cee5 100644
--- a/conf/onos.properties
+++ b/conf/onos.properties
@@ -5,7 +5,8 @@
 net.onrc.onos.core.flowprogrammer.FlowProgrammer,\
 net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule,\
 net.onrc.onos.core.intent.runtime.PlanInstallModule,\
-net.onrc.onos.core.registry.ZookeeperRegistry
+net.onrc.onos.core.registry.ZookeeperRegistry, \
+net.onrc.onos.core.metrics.OnosMetricsModule
 net.floodlightcontroller.restserver.RestApiServer.port = 8080
 net.floodlightcontroller.core.FloodlightProvider.openflowport = 6633
 net.floodlightcontroller.core.FloodlightProvider.workerthreads = 16
diff --git a/pom.xml b/pom.xml
index bced4a7..61f8a8c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,7 @@
     <github.global.server>github</github.global.server>
      -->
     <hazelcast.version>3.2.3</hazelcast.version>
+    <metrics.version>3.0.1</metrics.version>
   </properties>
   <build>
     <plugins>
@@ -634,6 +635,16 @@
       <artifactId>findbugs</artifactId>
       <version>${findbugs.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.codahale.metrics</groupId>
+      <artifactId>metrics-core</artifactId>
+      <version>${metrics.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.codahale.metrics</groupId>
+      <artifactId>metrics-json</artifactId>
+      <version>${metrics.version}</version>
+    </dependency>
     <!-- Floodlight's dependencies -->
     <dependency>
       <groupId>args4j</groupId>
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsObjectResource.java b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectResource.java
new file mode 100644
index 0000000..efb0213
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectResource.java
@@ -0,0 +1,283 @@
+package net.onrc.onos.core.metrics;
+
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.Histogram;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.util.List;
+
+/**
+ * Resource class to hold Metrics information.  Timers, Gauges, Counters,
+ * Meters and Historgrams are currently implemented.
+ */
+@JsonSerialize(using = MetricsObjectSerializer.class)
+@SuppressWarnings("rawtypes")
+public class MetricsObjectResource {
+
+    /**
+     * Base Metric object that all metrics inherit from.  Defines common
+     * attributes.
+     */
+    static class BaseMetricObject {
+        private final String name;
+
+        /**
+         * Constructor for the base object.  Sets the name attribute.
+         *
+         * @param newName name of the Metric
+         */
+        protected BaseMetricObject(final String newName) {
+            name = newName;
+        }
+
+        /**
+         * Get the name of the Metric.
+         *
+         * @return metric name
+         */
+        public String getName() {
+            return name;
+        }
+    }
+
+    /**
+     * Metric object that represents a Timer.
+     */
+    static class TimerObjectResource extends BaseMetricObject {
+        private final Timer timer;
+
+        /**
+         * Construct a new Timer resource object.
+         *
+         * @param newName name to use for the timer
+         * @param newTimer Metrics Timer object
+         */
+        public TimerObjectResource(final String newName,
+                                   final Timer newTimer) {
+            super(newName);
+            timer = newTimer;
+        }
+
+        /**
+         * Get the Metrics Timer object for this resource.
+         *
+         * @return Metrics Timer object.
+         */
+        public Timer getTimer() {
+            return timer;
+        }
+    }
+
+    /**
+     * Metric object that represents a Gauge.
+     */
+    static class GaugeObjectResource extends BaseMetricObject {
+        private final Gauge gauge;
+
+        /**
+         * Constructs a new Gauge resource object.
+         *
+         * @param newName name to use for the Gauge object
+         * @param newGauge Metrics Gauge object
+         */
+        public GaugeObjectResource(final String newName,
+                                   final Gauge newGauge) {
+            super(newName);
+            gauge = newGauge;
+        }
+
+        /**
+         * Gets the Metrics Gauge object for this resource.
+         *
+         * @return Metrics Gauge object.
+         */
+        public Gauge getGauge() {
+            return gauge;
+        }
+    }
+
+    /**
+     * Metric object that represents a Counter.
+     */
+    static class CounterObjectResource extends BaseMetricObject {
+        private final Counter counter;
+
+        /**
+         * Constructs a new Counter resource object.
+         *
+         * @param newName name to use for the Counter object
+         * @param newCounter Metrics Counter object
+         */
+        public CounterObjectResource(final String newName,
+                                     final Counter newCounter) {
+            super(newName);
+            counter = newCounter;
+        }
+
+        /**
+         * Gets the Metrics Counter object for this resource.
+         *
+         * @return Metrics Counter object.
+         */
+        public Counter getCounter() {
+            return counter;
+        }
+    }
+
+    /**
+     * Metric object that represents a Meter.
+     */
+    static class MeterObjectResource extends BaseMetricObject {
+        private final Meter meter;
+
+        /**
+         * Constructs a new Meter resource object.
+         *
+         * @param newName name to use for the Meter object
+         * @param newMeter Metrics Meter object
+         */
+        public MeterObjectResource(final String newName,
+                                   final Meter newMeter) {
+            super(newName);
+            meter = newMeter;
+        }
+
+        /**
+         * Gets the Metrics Meter object for this resource.
+         *
+         * @return Metrics Meter object.
+         */
+        public Meter getMeter() {
+            return meter;
+        }
+    }
+
+    /**
+     * Metric objerct that represents a Histogram.
+     */
+    static class HistogramObjectResource extends BaseMetricObject {
+        private final Histogram histogram;
+
+        /**
+         * Constructs a new Histogram resource object.
+         *
+         * @param newName name to use for Histogram object.
+         * @param newHistogram Metrics Histogram object.
+         */
+        public HistogramObjectResource(final String newName,
+                                       final Histogram newHistogram) {
+            super(newName);
+            histogram = newHistogram;
+        }
+
+        /**
+         * Gets the Metrics Histogram object for this resource.
+         *
+         * @return Metrics Histogram Object
+         */
+        public Histogram getHistogram() {
+            return histogram;
+        }
+    }
+
+
+    private List<TimerObjectResource> timers;
+    private List<GaugeObjectResource> gauges;
+    private List<CounterObjectResource> counters;
+    private List<MeterObjectResource> meters;
+    private List<HistogramObjectResource> histograms;
+
+    /**
+     * Gets the list of Gauge objects.
+     *
+     * @return list of gauges
+     */
+    public List<GaugeObjectResource> getGauges() {
+        return gauges;
+    }
+
+    /**
+     * Defines the list of Gauge objects.
+     *
+     * @param gauges list of gauges
+     */
+    public void setGauges(List<GaugeObjectResource> gauges) {
+        this.gauges = gauges;
+    }
+
+    /**
+     * Gets the list of Timer objects.
+     *
+     * @return list of Timers
+     */
+    public List<TimerObjectResource> getTimers() {
+        return timers;
+    }
+
+    /**
+     * Defines the list of Timer objects.
+     *
+     * @param newTimers list of Timers
+     */
+    public void setTimers(List<TimerObjectResource> newTimers) {
+        timers = newTimers;
+    }
+
+    /**
+     * Gets the list of Counter objects.
+     *
+     * @return list of Counters
+     */
+    public List<CounterObjectResource> getCounters() {
+        return counters;
+    }
+
+    /**
+     * Defines the list of Counter objects.
+     *
+     * @param counters list of Counters
+     */
+    public void setCounters(List<CounterObjectResource> counters) {
+        this.counters = counters;
+    }
+
+    /**
+     * Gets the list of Meter objects.
+     *
+     * @return list of Meters
+     */
+    public List<MeterObjectResource> getMeters() {
+        return meters;
+    }
+
+    /**
+     * Defines the list of Meter objects.
+     *
+     * @param meters list of Meters
+     */
+    public void setMeters(List<MeterObjectResource> meters) {
+        this.meters = meters;
+    }
+
+    /**
+     * Gets the list of Histogram objects.
+     *
+     * @return list of Histograms
+     */
+    public List<HistogramObjectResource> getHistograms() {
+        return histograms;
+    }
+
+    /**
+     * Defines the list of Histogram objects.
+     *
+     * @param histograms list of Histograms.
+     */
+    public void setHistograms(List<HistogramObjectResource> histograms) {
+        this.histograms = histograms;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsObjectSerializer.java b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectSerializer.java
new file mode 100644
index 0000000..ba32381
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectSerializer.java
@@ -0,0 +1,123 @@
+package net.onrc.onos.core.metrics;
+
+import com.codahale.metrics.json.MetricsModule;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * JSON serializer for the Metrics resource.
+ */
+public class MetricsObjectSerializer extends SerializerBase<MetricsObjectResource> {
+
+    /**
+     * Public constructor - just calls its super class constructor.
+     */
+    public MetricsObjectSerializer() {
+        super(MetricsObjectResource.class);
+    }
+
+    /**
+     * Convenience method to serialize a Metrics field.
+     *
+     * @param jsonGenerator generator to use for serialization
+     * @param fieldName name of the top level field
+     * @param serializedObjectJSON JSON representation from the Metrics serializer
+     * @param object internal resource for the Metric
+     * @throws IOException if JSON generation fails.
+     */
+    private void serializeItem(final JsonGenerator jsonGenerator,
+                               final String fieldName,
+                               final String serializedObjectJSON,
+                               final MetricsObjectResource.BaseMetricObject object)
+            throws IOException {
+        jsonGenerator.writeStartObject();
+        jsonGenerator.writeStringField("name", object.getName());
+
+        // If you write the JSON for the Metric using a StringField, the
+        // generator applies an extra set of double quotes and breaks the
+        // syntax.  You have to use the raw JSON output to get it right.
+        jsonGenerator.writeRaw(",\"" + fieldName + "\": " + serializedObjectJSON);
+        jsonGenerator.writeEndObject();
+    }
+
+    /**
+     * Serialize a MetricsObjectResource into JSON.  For each kind of Metric,
+     * his serializes common ONOS defined fields like name and
+     * then calls the Metrics serializer to make the JSON string
+     * for the actual Metric.
+     *
+     * @param metrics resource for all ONOS Metrics
+     * @param jsonGenerator generator to use for the JSON output
+     * @param serializerProvider unused, needed for Override
+     * @throws IOException if any of the JSON serializations fail
+     */
+    @Override
+    @SuppressWarnings("rawtypes")
+    public void serialize(final MetricsObjectResource metrics,
+                          final JsonGenerator jsonGenerator,
+                          final SerializerProvider serializerProvider)
+            throws IOException {
+
+        final ObjectMapper mapper = new ObjectMapper().registerModule(
+                new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, false));
+        jsonGenerator.writeStartObject();
+
+        //  serialize Timers
+        jsonGenerator.writeArrayFieldStart("timers");
+
+        for (final MetricsObjectResource.TimerObjectResource timer :
+             metrics.getTimers()) {
+            final String timerJSON = mapper.writeValueAsString(timer.getTimer());
+            serializeItem(jsonGenerator, "timer", timerJSON, timer);
+        }
+        jsonGenerator.writeEndArray();
+
+        // Serialize Gauges
+        jsonGenerator.writeArrayFieldStart("gauges");
+
+        for (final MetricsObjectResource.GaugeObjectResource gauge :
+             metrics.getGauges()) {
+            final String gaugeJSON = mapper.writeValueAsString(gauge.getGauge());
+            serializeItem(jsonGenerator, "gauge", gaugeJSON, gauge);
+        }
+        jsonGenerator.writeEndArray();
+
+        // Serialize Counters
+        jsonGenerator.writeArrayFieldStart("counters");
+
+        for (final MetricsObjectResource.CounterObjectResource counter :
+             metrics.getCounters()) {
+            final String counterJSON = mapper.writeValueAsString(counter.getCounter());
+            serializeItem(jsonGenerator, "counter", counterJSON, counter);
+        }
+        jsonGenerator.writeEndArray();
+
+        // Serialize Meters
+        jsonGenerator.writeArrayFieldStart("meters");
+
+        for (final MetricsObjectResource.MeterObjectResource meter :
+             metrics.getMeters()) {
+            final String meterJSON = mapper.writeValueAsString(meter.getMeter());
+            serializeItem(jsonGenerator, "meter", meterJSON, meter);
+        }
+        jsonGenerator.writeEndArray();
+
+        // Serialize Histograms
+        jsonGenerator.writeArrayFieldStart("histograms");
+
+        for (final MetricsObjectResource.HistogramObjectResource histogram :
+             metrics.getHistograms()) {
+            final String histogramJSON = mapper.writeValueAsString(histogram.getHistogram());
+            serializeItem(jsonGenerator, "histogram", histogramJSON, histogram);
+        }
+        jsonGenerator.writeEndArray();
+
+        jsonGenerator.writeEndObject();
+   }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsResource.java b/src/main/java/net/onrc/onos/core/metrics/MetricsResource.java
new file mode 100644
index 0000000..64b7996
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsResource.java
@@ -0,0 +1,82 @@
+package net.onrc.onos.core.metrics;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Histogram;
+import org.restlet.representation.Representation;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * REST APIs for Metrics objects.
+ */
+public class MetricsResource extends ServerResource {
+
+    /**
+     * REST API to get all of the system's metrics.
+     *
+     * @return a Representation object containing the metrics
+     */
+    @Get("json")
+    @SuppressWarnings("rawtypes")
+    public Representation retrieve() throws Exception {
+        final MetricRegistry registry = OnosMetrics.getMetricsRegistry();
+        final MetricsObjectResource result = new MetricsObjectResource();
+
+        final List<MetricsObjectResource.TimerObjectResource> timers =
+                new ArrayList<>();
+        final List<MetricsObjectResource.GaugeObjectResource> gauges =
+                new ArrayList<>();
+        final List<MetricsObjectResource.CounterObjectResource> counters =
+                new ArrayList<>();
+        final List<MetricsObjectResource.MeterObjectResource> meters =
+                new ArrayList<>();
+        final List<MetricsObjectResource.HistogramObjectResource> histograms =
+                new ArrayList<>();
+
+        for (final Map.Entry<String, Timer> timer :
+             registry.getTimers().entrySet()) {
+            timers.add(new MetricsObjectResource.TimerObjectResource(
+                    timer.getKey(), timer.getValue()));
+        }
+        result.setTimers(timers);
+
+        for (final Map.Entry<String, Gauge> gauge :
+             registry.getGauges().entrySet()) {
+            gauges.add(new MetricsObjectResource.GaugeObjectResource(
+                    gauge.getKey(), gauge.getValue()));
+        }
+        result.setGauges(gauges);
+
+        for (final Map.Entry<String, Counter> counter :
+             registry.getCounters().entrySet()) {
+            counters.add(new MetricsObjectResource.CounterObjectResource(
+                    counter.getKey(), counter.getValue()));
+        }
+        result.setCounters(counters);
+
+        for (final Map.Entry<String, Meter> meter :
+             registry.getMeters().entrySet()) {
+            meters.add(new MetricsObjectResource.MeterObjectResource(
+                    meter.getKey(), meter.getValue()));
+        }
+        result.setMeters(meters);
+
+        for (final Map.Entry<String, Histogram> histogram :
+             registry.getHistograms().entrySet()) {
+            histograms.add(new MetricsObjectResource.HistogramObjectResource(
+                    histogram.getKey(), histogram.getValue()));
+        }
+        result.setHistograms(histograms);
+
+        return toRepresentation(result, null);
+    }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsWebRoutable.java b/src/main/java/net/onrc/onos/core/metrics/MetricsWebRoutable.java
new file mode 100644
index 0000000..2761e71
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsWebRoutable.java
@@ -0,0 +1,31 @@
+package net.onrc.onos.core.metrics;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * Restlet Router for Metrics REST APIs.
+ */
+public class MetricsWebRoutable implements RestletRoutable {
+    /**
+     * Creates the Restlet router and binds to the proper resources.
+     */
+    @Override
+    public Restlet getRestlet(Context context) {
+        Router router = new Router(context);
+        // GET all metrics
+        router.attach("", MetricsResource.class);
+        return router;
+    }
+
+
+    /**
+     * Sets the base path for the Metrics.
+     */
+    @Override
+    public String basePath() {
+        return "/wm/onos/metrics";
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/OnosMetrics.java b/src/main/java/net/onrc/onos/core/metrics/OnosMetrics.java
new file mode 100644
index 0000000..bfd603f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/OnosMetrics.java
@@ -0,0 +1,28 @@
+package net.onrc.onos.core.metrics;
+
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * This class acts a singleton to hold the Metrics registry for ONOS.
+ */
+public final class OnosMetrics {
+
+    /**
+     * Hide constructor.  The only way to get the registry is through the
+     * singleton getter.
+     */
+    private OnosMetrics() {}
+
+    private static final MetricRegistry METRICS_REGISTRY = new MetricRegistry();
+
+    /**
+     * Get the singleton Metrics registry.  A single instance of
+     * the registry is statically allocated and then used by all callers.
+     *
+     * @return Metrics registry
+     */
+    public static MetricRegistry getMetricsRegistry() {
+        return METRICS_REGISTRY;
+    }
+}
+
diff --git a/src/main/java/net/onrc/onos/core/metrics/OnosMetricsModule.java b/src/main/java/net/onrc/onos/core/metrics/OnosMetricsModule.java
new file mode 100644
index 0000000..6f046cf
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/OnosMetricsModule.java
@@ -0,0 +1,53 @@
+package net.onrc.onos.core.metrics;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.onrc.onos.core.registry.IControllerRegistryService;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Floodlight module to add the REST APIs for Metrics.
+ */
+public class OnosMetricsModule implements IFloodlightModule {
+
+    private IRestApiService restApi;
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        return null;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService>
+           getServiceImpls() {
+        return null;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        List<Class<? extends IFloodlightService>> dependencies =
+                new ArrayList<>();
+        dependencies.add(IControllerRegistryService.class);
+        dependencies.add(IRestApiService.class);
+        return dependencies;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context)
+            throws FloodlightModuleException {
+        restApi = context.getServiceImpl(IRestApiService.class);
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) {
+        restApi.addRestletRoutable(new MetricsWebRoutable());
+    }
+}
+
diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
index 8460383..28c4a83 100644
--- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
+++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
@@ -18,3 +18,4 @@
 net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule
 net.onrc.onos.core.intent.runtime.PlanInstallModule
 net.onrc.onos.core.packetservice.PacketModule
+net.onrc.onos.core.metrics.OnosMetricsModule
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestMetrics.java b/src/test/java/net/onrc/onos/api/rest/TestRestMetrics.java
new file mode 100644
index 0000000..6b5f453
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestMetrics.java
@@ -0,0 +1,119 @@
+package net.onrc.onos.api.rest;
+
+import com.codahale.metrics.Clock;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.onrc.onos.core.intent.runtime.IntentTestMocks;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.metrics.MetricsWebRoutable;
+import net.onrc.onos.core.topology.ITopologyService;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Test harness for Metrics based REST API tests.  This class maintains the
+ * web server and mocks required for testing metrics APIs.  REST API tests
+ * for metrics should inherit from this class.
+ */
+public class TestRestMetrics extends TestRest {
+
+    private IntentTestMocks mocks;
+
+    /**
+     * Fetch the Intent mocking object.
+     *
+     * @return intent mocking object
+     */
+    IntentTestMocks getMocks() {
+        return mocks;
+    }
+
+    /**
+     * Create the web server and mocks required for the topology tests.
+     */
+    @Override
+    public void setUp() {
+        mocks = new IntentTestMocks();
+        mocks.setUpIntentMocks();
+
+        addRestlet(new MetricsWebRoutable());
+        super.setUp();
+
+        final PathCalcRuntimeModule runtime = new PathCalcRuntimeModule();
+        final FloodlightModuleContext moduleContext = getMocks().getModuleContext();
+        try {
+            runtime.init(moduleContext);
+        } catch (FloodlightModuleException floodlightEx) {
+            throw new IllegalArgumentException(floodlightEx);
+        }
+        runtime.startUp(moduleContext);
+
+        getRestApiServer().addAttribute(ITopologyService.class.getCanonicalName(),
+                mocks.getTopologyService());
+    }
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @Override
+    public void tearDown() {
+        getMocks().tearDownIntentMocks();
+        super.tearDown();
+    }
+
+    /**
+     * Fetch the base URL for Metrics REST APIs.
+     *
+     * @return base URL
+     */
+    String getBaseRestMetricsUrl() {
+        return getBaseRestUrl() + "/metrics";
+    }
+
+    /**
+     * Check that the given list of elements in a JSON object are all 0 length
+     * arrays.
+     *
+     * @param elementNames names of top level elements to check
+     * @param jsonObject top level JSON object
+     * @throws JSONException if JSON fetching throws an error
+     */
+    public void checkEmptyLists(final JSONObject jsonObject,
+                                final String ... elementNames)
+                throws JSONException {
+        for (final String elementName : elementNames) {
+            final JSONArray element = jsonObject.getJSONArray(elementName);
+            assertThat(element, is(notNullValue()));
+            assertThat(element.length(), is(0));
+        }
+    }
+
+    public static final int MOCK_CLOCK_MILISECONDS_PER_TICK = 50;
+
+    /**
+     * Mock clock used for Timer and Meter tests to give known time values for
+     * test data.  Each simulated tick increments the time by
+     * MOCK_CLOCK_MILISECONDS_PER_TICK which is currently defined for 50
+     * millisecond ticks.
+     */
+    protected final Clock mockClock = new Clock() {
+        private long currentTime = 0;
+
+        @Override
+        public long getTick() {
+            final long tickInNanoseconds =
+                    TimeUnit.NANOSECONDS.convert(MOCK_CLOCK_MILISECONDS_PER_TICK,
+                                                 TimeUnit.MILLISECONDS);
+            currentTime = currentTime + tickInNanoseconds;
+            return currentTime;
+        }
+    };
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestMetricsCounters.java b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsCounters.java
new file mode 100644
index 0000000..84fb4db
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsCounters.java
@@ -0,0 +1,136 @@
+package net.onrc.onos.api.rest;
+
+import com.codahale.metrics.Counter;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.metrics.OnosMetrics;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.restlet.resource.ClientResource;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests for REST APIs for Counter Metrics.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestMetricsCounters extends TestRestMetrics {
+
+    /**
+     * Create the web server and mocks required for
+     * all of the tests.
+     */
+    @Before
+    @SuppressWarnings("ununsed")
+    public void beforeTest() {
+        setRestPort(generateRandomPort());
+        setUp();
+    }
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @After
+    @SuppressWarnings("unused")
+    public void afterTest() {
+        tearDown();
+    }
+
+    //  Test Counter data objects
+    private static final String COUNTER1_NAME = "COUNTER1";
+    private static final int COUNTER1_COUNT = 0;
+
+    private static final String COUNTER2_NAME = "COUNTER2";
+    private static final int COUNTER2_COUNT = -1;
+
+    private static final String COUNTER3_NAME = "COUNTER3";
+    private static final int COUNTER3_COUNT = 5;
+
+    private final Counter counter1 =
+            OnosMetrics.getMetricsRegistry().counter(COUNTER1_NAME);
+    private final Counter counter2 =
+            OnosMetrics.getMetricsRegistry().counter(COUNTER2_NAME);
+    private final Counter counter3 =
+            OnosMetrics.getMetricsRegistry().counter(COUNTER3_NAME);
+
+    /**
+     * Create some test data for the tests.
+     */
+    private void fillCounters() {
+        counter1.inc(COUNTER1_COUNT);
+        counter2.inc(COUNTER2_COUNT);
+        counter3.inc(COUNTER3_COUNT);
+    }
+
+    /**
+     * Check that a Counter object has the right contents.
+     *
+     * @param counterContainer JSON for the Counter
+     * @param name name of the Counter
+     * @param count expected count for the Counter
+     * @throws JSONException if any of the JSON fetches fail
+     */
+    private void checkCounter(final JSONObject counterContainer,
+                              final String name,
+                              final int count) throws JSONException {
+        final String counterName = counterContainer.getString("name");
+        assertThat(counterName, is(notNullValue()));
+        assertThat(counterName, is(equalTo(name)));
+
+        final JSONObject counterObject = counterContainer.getJSONObject("counter");
+        assertThat(counterObject, is(notNullValue()));
+
+        final int counterValue = counterObject.getInt("count");
+        assertThat(counterValue, is(equalTo(count)));
+    }
+
+    /**
+     * Test the REST APIs for Metrics Counter objects.
+     *
+     * @throws JSONException
+     */
+    @Test
+    public void testCounters() throws JSONException {
+
+        fillCounters();
+
+        //  Read the metrics from the REST API for the test data
+        final ClientResource client = new ClientResource(getBaseRestMetricsUrl());
+
+        final JSONObject metrics = getJSONObject(client);
+        assertThat(metrics.length(), is(equalTo(5)));
+
+        //  There should be 3 counters
+        final JSONArray counters = metrics.getJSONArray("counters");
+        assertThat(counters, is(notNullValue()));
+        assertThat(counters.length(), is(3));
+
+        //  There should be no timers, gauges, meters or histograms
+        checkEmptyLists(metrics, "timers", "gauges", "meters", "histograms");
+
+        //  Check the values for counter 1
+        final JSONObject counter1Container = counters.getJSONObject(0);
+        checkCounter(counter1Container, COUNTER1_NAME, COUNTER1_COUNT);
+
+        //  Check the values for counter 1
+        final JSONObject counter2Container = counters.getJSONObject(1);
+        checkCounter(counter2Container, COUNTER2_NAME, COUNTER2_COUNT);
+
+        //  Check the values for counter 1
+        final JSONObject counter3Container = counters.getJSONObject(2);
+        checkCounter(counter3Container, COUNTER3_NAME, COUNTER3_COUNT);
+
+    }
+
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestMetricsGauges.java b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsGauges.java
new file mode 100644
index 0000000..91cc86f
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsGauges.java
@@ -0,0 +1,148 @@
+package net.onrc.onos.api.rest;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.metrics.OnosMetrics;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.restlet.resource.ClientResource;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests for REST APIs for Gauges Metrics.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestMetricsGauges extends TestRestMetrics {
+
+    /**
+     * Create the web server and mocks required for
+     * all of the tests.
+     */
+    @Before
+    @SuppressWarnings("ununsed")
+    public void beforeTest() {
+        setRestPort(generateRandomPort());
+        setUp();
+    }
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @After
+    @SuppressWarnings("unused")
+    public void afterTest() {
+        tearDown();
+    }
+
+    // Test data for Gauges
+    private static final String GAUGE1_NAME = "gauge1";
+    private static final int GAUGE1_VALUE = 0;
+
+    private static final String GAUGE2_NAME = "gauge2";
+    private static final int GAUGE2_VALUE = -1;
+
+    private static final String GAUGE3_NAME = "gauge3";
+    private static final int GAUGE3_VALUE = 123456789;
+
+    private final Gauge<Integer> gauge1 = OnosMetrics.getMetricsRegistry().
+            register(MetricRegistry.name(GAUGE1_NAME),
+                    new Gauge<Integer>() {
+                        @Override
+                        public Integer getValue() {
+                            return GAUGE1_VALUE;
+                        }
+                    });
+
+    private final Gauge<Integer> gauge2 = OnosMetrics.getMetricsRegistry().
+            register(MetricRegistry.name(GAUGE2_NAME),
+                    new Gauge<Integer>() {
+                        @Override
+                        public Integer getValue() {
+                            return GAUGE2_VALUE;
+                        }
+                    });
+
+    private final Gauge<Integer> gauge3 = OnosMetrics.getMetricsRegistry().
+            register(MetricRegistry.name(GAUGE3_NAME),
+                    new Gauge<Integer>() {
+                        @Override
+                        public Integer getValue() {
+                            return GAUGE3_VALUE;
+                        }
+                    });
+
+    /**
+     * Check that the JSON for a Gauge obect has the correct data values.
+     *
+     * @param gaugeContainer JSON object for the Gauge
+     * @param name expected name of the gauge
+     * @param gauge Metrics Gauge object that hold the expected value
+     * @throws JSONException if any JSON operation fails
+     */
+    private void checkGauge(final JSONObject gaugeContainer,
+                            final String name,
+                            final Gauge<Integer> gauge)
+                 throws JSONException {
+        assertThat(gaugeContainer, is(notNullValue()));
+
+        final String gaugeName = gaugeContainer.getString("name");
+        assertThat(gaugeName, is(notNullValue()));
+        assertThat(gaugeName, is(equalTo(name)));
+
+        final JSONObject gaugeObject = gaugeContainer.getJSONObject("gauge");
+        assertThat(gaugeObject, is(notNullValue()));
+
+        final int gaugeValue = gaugeObject.getInt("value");
+        assertThat(gaugeValue, is(equalTo(gauge.getValue())));
+    }
+
+    /**
+     * Unit test for the Gauges portion of the Metrics REST API.
+     *
+     * @throws JSONException if any JSON operation fails
+     */
+    @Test
+    public void testGauges() throws Exception {
+
+        //  Read the metrics from the REST API for the test data
+        final ClientResource client = new ClientResource(getBaseRestMetricsUrl());
+
+        final JSONObject metrics = getJSONObject(client);
+        assertThat(metrics.length(), is(equalTo(5)));
+
+        //  There should be 3 gauges
+        final JSONArray gauges = metrics.getJSONArray("gauges");
+        assertThat(gauges, is(notNullValue()));
+        assertThat(gauges.length(), is(3));
+
+        //  There should be no timers, meters, histograms or counters
+        checkEmptyLists(metrics, "timers", "meters", "histograms", "counters");
+
+        //  Check the values for gauge 1
+        final JSONObject gauge1Container = gauges.getJSONObject(0);
+        checkGauge(gauge1Container, GAUGE1_NAME, gauge1);
+
+        //  Check the values for gauge 2
+        final JSONObject gauge2Container = gauges.getJSONObject(1);
+        checkGauge(gauge2Container, GAUGE2_NAME, gauge2);
+
+        //  Check the values for gauge 3
+        final JSONObject gauge3Container = gauges.getJSONObject(2);
+        checkGauge(gauge3Container, GAUGE3_NAME, gauge3);
+    }
+
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestMetricsHistograms.java b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsHistograms.java
new file mode 100644
index 0000000..ae528db
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsHistograms.java
@@ -0,0 +1,204 @@
+package net.onrc.onos.api.rest;
+
+import com.codahale.metrics.Histogram;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.metrics.OnosMetrics;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.restlet.resource.ClientResource;
+
+import java.util.Arrays;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.both;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests for REST APIs for Histogram Metrics.
+ */
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestMetricsHistograms extends TestRestMetrics {
+
+    /**
+     * Create the web server and mocks required for
+     * all of the tests.
+     */
+    @Before
+    @SuppressWarnings("ununsed")
+    public void beforeTest() {
+        setRestPort(generateRandomPort());
+        setUp();
+    }
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @After
+    @SuppressWarnings("unused")
+    public void afterTest() {
+        tearDown();
+    }
+
+    // Test data for Histograms
+
+    private static final String HISTOGRAM1_NAME = "HISTOGRAM1";
+    private static final int[] HISTOGRAM1_VALUES =
+            {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+    private static final String HISTOGRAM2_NAME = "HISTOGRAM2";
+    private static final int[] HISTOGRAM2_VALUES =
+            {100, 100, 100, 100, 100, 100, 100};
+
+    private static final String HISTOGRAM3_NAME = "HISTOGRAM3";
+    private static final int[] HISTOGRAM3_VALUES =
+            {555};
+
+    private final Histogram histogram1 =
+            OnosMetrics.getMetricsRegistry().histogram(HISTOGRAM1_NAME);
+    private final Histogram histogram2 =
+            OnosMetrics.getMetricsRegistry().histogram(HISTOGRAM2_NAME);
+    private final Histogram histogram3 =
+            OnosMetrics.getMetricsRegistry().histogram(HISTOGRAM3_NAME);
+
+    /**
+     * Add each int in an array to a Histogram.
+     *
+     * @param histogram Histogram object to update
+     * @param values list of values to add to the Histogram
+     */
+    private void updateHistogramFromArray(final Histogram histogram,
+                                          final int[] values) {
+        for (final int value : values) {
+            histogram.update(value);
+        }
+    }
+
+    /**
+     * Initialize all the Histograms.
+     */
+    private void fillHistograms() {
+        updateHistogramFromArray(histogram1, HISTOGRAM1_VALUES);
+        updateHistogramFromArray(histogram2, HISTOGRAM2_VALUES);
+        updateHistogramFromArray(histogram3, HISTOGRAM3_VALUES);
+    }
+
+    /**
+     * Check that a JSON object representing a histogram contains the correct
+     * data.
+     *
+     * @param histogramContainer JSON object for the Histogram
+     * @param name the name of the Histogram
+     * @param values the array of expected values in the histogram
+     * @throws JSONException if any of the JSON processing generates an error
+     */
+    private void checkHistogram(final JSONObject histogramContainer,
+                              final String name,
+                              final int[] values) throws JSONException {
+
+        // Check that the name is correct
+        final String histogramName = histogramContainer.getString("name");
+        assertThat(histogramName, is(notNullValue()));
+        assertThat(histogramName, is(equalTo(name)));
+
+        //  Make sure a histogram is present
+        final JSONObject histogramObject = histogramContainer.getJSONObject("histogram");
+        assertThat(histogramObject, is(notNullValue()));
+
+        // The histogram count should equal the length of the array used to
+        // initialize it.
+        final int histogramCount = histogramObject.getInt("count");
+        assertThat(histogramCount, is(equalTo(values.length)));
+
+        final int[] sortedValues = Arrays.copyOf(values, values.length);
+        Arrays.sort(sortedValues);
+
+        // max should be the largest value from the array
+        final int max = histogramObject.getInt("max");
+        assertThat(max, is(equalTo(sortedValues[sortedValues.length - 1])));
+
+        // min should be the smallest value from the array
+        final int min = histogramObject.getInt("min");
+        assertThat(min, is(equalTo(sortedValues[0])));
+
+        // Each of the probability values should be between the min and the max
+        // value
+        final double p999 = histogramObject.getDouble("p999");
+        assertThat((int) p999,
+                is(both(greaterThanOrEqualTo(min)).and(lessThanOrEqualTo(max))));
+
+        final double p99 = histogramObject.getDouble("p99");
+        assertThat((int) p99,
+                is(both(greaterThanOrEqualTo(min)).and(lessThanOrEqualTo(max))));
+
+        final double p98 = histogramObject.getDouble("p98");
+        assertThat((int) p98,
+                is(both(greaterThanOrEqualTo(min)).and(lessThanOrEqualTo(max))));
+
+        final double p95 = histogramObject.getDouble("p95");
+        assertThat((int) p95,
+                is(both(greaterThanOrEqualTo(min)).and(lessThanOrEqualTo(max))));
+
+        final double p75 = histogramObject.getDouble("p75");
+        assertThat((int) p75,
+                is(both(greaterThanOrEqualTo(min)).and(lessThanOrEqualTo(max))));
+
+        final double p50 = histogramObject.getDouble("p50");
+        assertThat((int) p50,
+                is(both(greaterThanOrEqualTo(min)).and(lessThanOrEqualTo(max))));
+
+    }
+
+    /**
+     * Unit test for REST APIs for Histogram Metrics.
+     *
+     * @throws JSONException if any JSON processing causes an error
+     */
+    @Test
+    public void testHistograms() throws JSONException {
+
+        fillHistograms();
+
+        //  Read the metrics from the REST API for the test data
+        final ClientResource client = new ClientResource(getBaseRestMetricsUrl());
+
+        final JSONObject metrics = getJSONObject(client);
+        assertThat(metrics.length(), is(equalTo(5)));
+
+        //  There should be 3 histograms
+        final JSONArray histograms = metrics.getJSONArray("histograms");
+        assertThat(histograms, is(notNullValue()));
+        assertThat(histograms.length(), is(3));
+
+        //  There should be no timers, gauges, meters or counters
+        checkEmptyLists(metrics, "timers", "gauges", "meters", "counters");
+
+        //  Check the values for histogram 1
+        final JSONObject histogram1Container = histograms.getJSONObject(0);
+        checkHistogram(histogram1Container, HISTOGRAM1_NAME, HISTOGRAM1_VALUES);
+
+        //  Check the values for histogram 2
+        final JSONObject histogram2Container = histograms.getJSONObject(1);
+        checkHistogram(histogram2Container, HISTOGRAM2_NAME, HISTOGRAM2_VALUES);
+
+        //  Check the values for histogram 3
+        final JSONObject histogram3Container = histograms.getJSONObject(2);
+        checkHistogram(histogram3Container, HISTOGRAM3_NAME, HISTOGRAM3_VALUES);
+
+    }
+
+}
+
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestMetricsMeters.java b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsMeters.java
new file mode 100644
index 0000000..c1b21b1
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsMeters.java
@@ -0,0 +1,191 @@
+package net.onrc.onos.api.rest;
+
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricSet;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.metrics.OnosMetrics;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.restlet.resource.ClientResource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.both;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests for REST APIs for Meter Metrics.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestMetricsMeters extends TestRestMetrics {
+
+    /**
+     * Create the web server and mocks required for
+     * all of the tests.
+     */
+    @Before
+    @SuppressWarnings("ununsed")
+    public void beforeTest() {
+        setRestPort(generateRandomPort());
+        setUp();
+    }
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @After
+    @SuppressWarnings("unused")
+    public void afterTest() {
+        tearDown();
+    }
+
+    //  Test data for Meters
+
+    private static final String METER1_NAME = "METER1";
+    private static final int METER1_ITERATIONS = 1;
+
+    private static final String METER2_NAME = "METER2";
+    private static final int METER2_ITERATIONS = 10;
+
+    private static final String METER3_NAME = "METER3";
+    private static final int METER3_ITERATIONS = 100;
+
+    private final Meter meter1 = new Meter(mockClock);
+    private final Meter meter2 = new Meter(mockClock);
+    private final Meter meter3 = new Meter(mockClock);
+
+    /**
+     * Fill in test data for a given Meter.
+     *
+     * @param meter Meter object to fill in
+     * @param iterations How many times to mark the meter
+     */
+    private void fillMeter(final Meter meter,
+                           final long iterations) {
+        for (int i = 0; i < iterations; i++) {
+            meter.mark();
+        }
+    }
+
+    /**
+     * Fill in test data in the test Meters.
+     */
+    private void fillMeters() {
+        fillMeter(meter1, METER1_ITERATIONS);
+        fillMeter(meter2, METER2_ITERATIONS);
+        fillMeter(meter3, METER3_ITERATIONS);
+
+        final MetricSet meterSet = new MetricSet() {
+            @Override
+            public Map<String, Metric> getMetrics() {
+                final Map<String, Metric> meters = new HashMap<>();
+                meters.put(METER1_NAME, meter1);
+                meters.put(METER2_NAME, meter2);
+                meters.put(METER3_NAME, meter3);
+                return meters;
+            }
+        };
+        OnosMetrics.getMetricsRegistry().registerAll(meterSet);
+    }
+
+    /**
+     * Check if the data in a Meter matches the expected values.
+     *
+     * @param meter the meter object that this JSON representation should match
+     * @param meterContainer JSON object for the Meter
+     * @param name name of the Meter
+     * @param iterations count of marks that should be in the Meter
+     * @throws JSONException if any of the JSON processing fails
+     */
+    private void checkMeter(final Meter meter,
+                            final JSONObject meterContainer,
+                            final String name,
+                            final int iterations)
+                 throws JSONException {
+        final String meterName = meterContainer.getString("name");
+        assertThat(meterName, is(notNullValue()));
+        assertThat(meterName, is(equalTo(name)));
+
+        final JSONObject meterObject = meterContainer.getJSONObject("meter");
+        assertThat(meterObject, is(notNullValue()));
+
+        final int meterCount = meterObject.getInt("count");
+        assertThat(meterCount, is(equalTo(iterations)));
+
+        final double m15Rate = meterObject.getDouble("m15_rate");
+        assertThat(m15Rate, is(equalTo(meter.getFifteenMinuteRate())));
+
+        final double m5Rate = meterObject.getDouble("m5_rate");
+        assertThat(m5Rate, is(equalTo(meter.getFiveMinuteRate())));
+
+        final double m1Rate = meterObject.getDouble("m1_rate");
+        assertThat(m1Rate, is(equalTo(meter.getOneMinuteRate())));
+
+        // mean should be between 0.0 and the longest rate.  Since all the rates
+        // are the same because of the mocked clock, use the 15 minute rate as
+        // the max - all the rates should be the same.
+        final double meanRate = meterObject.getDouble("mean_rate");
+        assertThat(meanRate,
+                is(both(greaterThanOrEqualTo(0.0)).
+                        and(lessThanOrEqualTo(m15Rate))));
+
+        final String units = meterObject.getString("units");
+        assertThat(units, is(equalTo("events/second")));
+    }
+
+    /**
+     * UNIT test for the Metrics REST API for Meters.
+     *
+     * @throws JSONException if any JSON processing fails
+     */
+    @Test
+    public void testMeters() throws JSONException {
+
+        fillMeters();
+
+        //  Read the metrics from the REST API for the test data
+        final ClientResource client = new ClientResource(getBaseRestMetricsUrl());
+
+        final JSONObject metrics = getJSONObject(client);
+        assertThat(metrics.length(), is(equalTo(5)));
+
+        //  There should be 3 meters
+        final JSONArray meters = metrics.getJSONArray("meters");
+        assertThat(meters, is(notNullValue()));
+        assertThat(meters.length(), is(3));
+
+        //  There should be no timers, gauges, histograms or counters
+        checkEmptyLists(metrics, "timers", "gauges", "histograms", "counters");
+
+        //  Check the values for meter 1
+        final JSONObject meter1Container = meters.getJSONObject(0);
+        checkMeter(meter1, meter1Container, METER1_NAME, METER1_ITERATIONS);
+
+        //  Check the values for meter 2
+        final JSONObject meter2Container = meters.getJSONObject(1);
+        checkMeter(meter2, meter2Container, METER2_NAME, METER2_ITERATIONS);
+
+        //  Check the values for meter 3
+        final JSONObject meter3Container = meters.getJSONObject(2);
+        checkMeter(meter3, meter3Container, METER3_NAME, METER3_ITERATIONS);
+
+    }
+
+}
+
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestMetricsTimers.java b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsTimers.java
new file mode 100644
index 0000000..e6670b4
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestMetricsTimers.java
@@ -0,0 +1,212 @@
+package net.onrc.onos.api.rest;
+
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricSet;
+import com.codahale.metrics.Reservoir;
+import com.codahale.metrics.SlidingWindowReservoir;
+import com.codahale.metrics.Timer;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.metrics.OnosMetrics;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.restlet.resource.ClientResource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.codahale.metrics.MetricRegistry.name;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.both;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThan;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Unit tests for REST APIs for Timer Metrics.
+ */
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestMetricsTimers extends TestRestMetrics {
+
+    /**
+     * Create the web server and mocks required for
+     * all of the tests.
+     */
+    @Before
+    @SuppressWarnings("ununsed")
+    public void beforeTest() {
+        setRestPort(generateRandomPort());
+        setUp();
+    }
+
+    //  Test data objects for Timers
+
+    //  timer1 will be called 3 times
+    private static final String TIMER1_NAME = name(TestRestMetricsTimers.class,
+                                                   "timer1");
+    private static final int TIMER1_COUNT = 3;
+
+    //  timer1 will be called 10 times
+    private static final String TIMER2_NAME = name(TestRestMetricsTimers.class,
+                                                   "timer2");
+    private static final int TIMER2_COUNT = 10;
+
+    private static final int RESERVOIR_SIZE = 100;
+    private Reservoir reservoir = new SlidingWindowReservoir(RESERVOIR_SIZE);
+
+    private final Timer timer1 = new Timer(reservoir, mockClock);
+    private final Timer timer2 = new Timer(reservoir, mockClock);
+
+    /**
+     * Fill in test data in the Timer objects.
+     */
+    private void fillTimers() {
+        // The mock clock will simulate ticks at MOCK_CLOCK_MILISECONDS_PER_TICK
+        // Each timer.time(), context.stop() pair will elapse
+        // MOCK_CLOCK_MILISECONDS_PER_TICK (currently 50 milliseconds)
+
+        // Initialize timer1 - 3 ticks, 50 milliseconds (simulted) apart
+        for (int i = 0; i < TIMER1_COUNT; i++) {
+            final Timer.Context context = timer1.time();
+            context.stop();
+        }
+
+        // Initialize timer2 - 10 ticks, 50 milliseconds (simulated) apart
+        for (int i = 0; i < TIMER2_COUNT; i++) {
+            final Timer.Context context = timer2.time();
+            context.stop();
+        }
+
+        // add the two timers to the registry so the REST APIs will pick them
+        // up
+        final MetricSet timerSet = new MetricSet() {
+            @Override
+            public Map<String, Metric> getMetrics() {
+                final Map<String, Metric> timers = new HashMap<>();
+                timers.put(TIMER1_NAME, timer1);
+                timers.put(TIMER2_NAME, timer2);
+                return timers;
+            }
+        };
+        OnosMetrics.getMetricsRegistry().registerAll(timerSet);
+    }
+
+    /**
+     * Check that the time values for a JSON Timer object are within the
+     * allowed range.
+     *
+     * @param timer JSON Timer object
+     * @param timeValue expected time value
+     * @throws JSONException if any of the JSON operations fail
+     */
+    private void assertThatTimesAreInRange(JSONObject timer, int timeValue)
+                 throws JSONException {
+
+        final double timerMax = timer.getDouble("max");
+        assertThat((int) timerMax,
+                is(both(greaterThanOrEqualTo(timeValue)).
+                        and(lessThan(timeValue + 5))));
+
+        final double timerMin = timer.getDouble("min");
+        assertThat((int) timerMin,
+                is(both(greaterThanOrEqualTo(timeValue)).
+                        and(lessThan(timeValue + 5))));
+
+        final double timerP99 = timer.getDouble("p99");
+        assertThat((int) timerP99,
+                is(both(greaterThanOrEqualTo(timeValue)).
+                        and(lessThan(timeValue + 5))));
+
+        final double timerP999 = timer.getDouble("p999");
+        assertThat((int) timerP999,
+                is(both(greaterThanOrEqualTo(timeValue)).
+                        and(lessThan(timeValue + 5))));
+    }
+
+    /**
+     * Remove anything that will interfere with the next test running correctly.
+     * Shuts down the test REST web server and removes the mocks.
+     */
+    @After
+    @SuppressWarnings("unused")
+    public void afterTest() {
+        tearDown();
+    }
+
+    /**
+     * Unit test for the REST APIs for Metrics Timers.
+     *
+     * @throws JSONException if any of the JSON processing fails.
+     */
+    @Test
+    public void testTimers() throws JSONException {
+        //  Make some test data
+        fillTimers();
+
+        //  Read the metrics from the REST API for the test data
+        final ClientResource client = new ClientResource(getBaseRestMetricsUrl());
+
+        final JSONObject metrics = getJSONObject(client);
+        assertThat(metrics.length(), is(equalTo(5)));
+
+        //  There should be 2 timers
+        final JSONArray timers = metrics.getJSONArray("timers");
+        assertThat(timers, is(notNullValue()));
+        assertThat(timers.length(), is(2));
+
+
+        //  There should be no historgramss, gauges, meters or counters
+        checkEmptyLists(metrics, "histograms", "gauges", "meters", "counters");
+
+        //  Check the values for timer 1
+        final JSONObject timer1Values = timers.getJSONObject(0);
+        assertThat(timer1Values, is(notNullValue()));
+
+        final String timer1Name = timer1Values.getString("name");
+        assertThat(timer1Name, is(notNullValue()));
+        assertThat(timer1Name, is(equalTo(TIMER1_NAME)));
+
+        final JSONObject timer1TimerObject = timer1Values.getJSONObject("timer");
+        assertThat(timer1TimerObject, is(notNullValue()));
+
+        final int timer1Count = timer1TimerObject.getInt("count");
+        assertThat(timer1Count, is(equalTo(TIMER1_COUNT)));
+
+        final String timer1DurationUnits = timer1TimerObject.getString("duration_units");
+        assertThat(timer1DurationUnits, is(equalTo("milliseconds")));
+
+        assertThatTimesAreInRange(timer1TimerObject,
+                                  MOCK_CLOCK_MILISECONDS_PER_TICK);
+
+        //  Check the values for timer 2
+        final JSONObject timer2Values = timers.getJSONObject(1);
+        assertThat(timer2Values, is(notNullValue()));
+
+        final String timer2Name = timer2Values.getString("name");
+        assertThat(timer2Name, is(notNullValue()));
+        assertThat(timer2Name, is(equalTo(TIMER2_NAME)));
+
+        final JSONObject timer2TimerObject = timer2Values.getJSONObject("timer");
+        assertThat(timer2TimerObject, is(notNullValue()));
+
+        final int timer2Count = timer2TimerObject.getInt("count");
+        assertThat(timer2Count, is(equalTo(TIMER2_COUNT)));
+
+        final String timer2DurationUnits = timer2TimerObject.getString("duration_units");
+        assertThat(timer2DurationUnits, is(equalTo("milliseconds")));
+
+        assertThatTimesAreInRange(timer2TimerObject,
+                                  MOCK_CLOCK_MILISECONDS_PER_TICK);
+    }
+
+}