Refeactor the Topology Events and Intent Events Metrics modules:
 * Use the new class EventMetric to cleanup and simplify the implementation
 * Replaced the single metric for Topology Events with four metrics
   (last event timestamp and event rate):
   - Device Event metrics
   - Host Event metrics
   - Link Event metrics
   - Topology Graph metrics

Change-Id: I2acc06ab84ef3ca06d0d383983dfcff9774ff341
diff --git a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java
index 54d3a95..b7e0401 100644
--- a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java
+++ b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java
@@ -11,6 +11,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.karaf.shell.commands.Command;
+import org.onlab.metrics.EventMetric;
 import org.onlab.onos.cli.AbstractShellCommand;
 import org.onlab.onos.metrics.topology.TopologyMetricsService;
 
@@ -22,15 +23,13 @@
 public class TopologyEventsMetricsCommand extends AbstractShellCommand {
 
     private static final String FORMAT_GAUGE =
-        "Last Topology Event Timestamp (ms from epoch)=%d";
+        "Topology %s Event Timestamp (ms from epoch)=%d";
     private static final String FORMAT_METER =
-        "Topology Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
+        "Topology %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
 
     @Override
     protected void execute() {
         TopologyMetricsService service = get(TopologyMetricsService.class);
-        Gauge<Long> gauge = service.lastEventTimestampEpochMsGauge();
-        Meter meter = service.eventRateMeter();
 
         if (outputJson()) {
             ObjectMapper mapper = new ObjectMapper()
@@ -38,32 +37,89 @@
                                                   TimeUnit.MILLISECONDS,
                                                   false));
             ObjectNode result = mapper.createObjectNode();
-            try {
-                //
-                // NOTE: The API for custom serializers is incomplete,
-                // hence we have to parse the JSON string to create JsonNode.
-                //
-                final String gaugeJson = mapper.writeValueAsString(gauge);
-                final String meterJson = mapper.writeValueAsString(meter);
-                JsonNode gaugeNode = mapper.readTree(gaugeJson);
-                JsonNode meterNode = mapper.readTree(meterJson);
-                result.put("lastTopologyEventTimestamp", gaugeNode);
-                result.put("topologyEventRate", meterNode);
-            } catch (JsonProcessingException e) {
-                log.error("Error writing value as JSON string", e);
-            } catch (IOException e) {
-                log.error("Error writing value as JSON string", e);
-            }
+            result = json(mapper, result, "topologyDeviceEvent",
+                          service.topologyDeviceEventMetric());
+            result = json(mapper, result, "topologyHostEvent",
+                          service.topologyHostEventMetric());
+            result = json(mapper, result, "topologyLinkEvent",
+                          service.topologyLinkEventMetric());
+            result = json(mapper, result, "topologyGraphEvent",
+                          service.topologyGraphEventMetric());
             print("%s", result);
         } else {
-            TimeUnit rateUnit = TimeUnit.SECONDS;
-            double rateFactor = rateUnit.toSeconds(1);
-            print(FORMAT_GAUGE, gauge.getValue());
-            print(FORMAT_METER, meter.getCount(),
-                  meter.getMeanRate() * rateFactor,
-                  meter.getOneMinuteRate() * rateFactor,
-                  meter.getFiveMinuteRate() * rateFactor,
-                  meter.getFifteenMinuteRate() * rateFactor);
+            printEventMetric("Device", service.topologyDeviceEventMetric());
+            printEventMetric("Host", service.topologyHostEventMetric());
+            printEventMetric("Link", service.topologyLinkEventMetric());
+            printEventMetric("Graph", service.topologyGraphEventMetric());
         }
     }
+
+    /**
+     * Produces JSON node for an Event Metric.
+     *
+     * @param mapper the JSON object mapper to use
+     * @param objectNode the JSON object node to use
+     * @param propertyPrefix the property prefix to use
+     * @param eventMetric the Event Metric with the data
+     * @return JSON object node for the Event Metric
+     */
+    private ObjectNode json(ObjectMapper mapper, ObjectNode objectNode,
+                            String propertyPrefix, EventMetric eventMetric) {
+        String gaugeName = propertyPrefix + "Timestamp";
+        String meterName = propertyPrefix + "Rate";
+        Gauge<Long> gauge = eventMetric.lastEventTimestampGauge();
+        Meter meter = eventMetric.eventRateMeter();
+
+        objectNode.put(gaugeName, json(mapper, gauge));
+        objectNode.put(meterName, json(mapper, meter));
+        return objectNode;
+    }
+
+    /**
+     * Produces JSON node for an Object.
+     *
+     * @param mapper the JSON object mapper to use
+     * @param object the Object with the data
+     * @return JSON node for the Object
+     */
+    private JsonNode json(ObjectMapper mapper, Object object) {
+        //
+        // NOTE: The API for custom serializers is incomplete,
+        // hence we have to parse the JSON string to create JsonNode.
+        //
+        try {
+            final String objectJson = mapper.writeValueAsString(object);
+            JsonNode jsonNode = mapper.readTree(objectJson);
+            return jsonNode;
+        } catch (JsonProcessingException e) {
+            log.error("Error writing value as JSON string", e);
+        } catch (IOException e) {
+            log.error("Error writing value as JSON string", e);
+        }
+        return null;
+    }
+
+    /**
+     * Prints an Event Metric.
+     *
+     * @param operationStr the string with the intent operation to print
+     * @param eventMetric the Event Metric to print
+     */
+    private void printEventMetric(String operationStr,
+                                  EventMetric eventMetric) {
+        Gauge<Long> gauge = eventMetric.lastEventTimestampGauge();
+        Meter meter = eventMetric.eventRateMeter();
+        TimeUnit rateUnit = TimeUnit.SECONDS;
+        double rateFactor = rateUnit.toSeconds(1);
+
+        // Print the Gauge
+        print(FORMAT_GAUGE, operationStr, gauge.getValue());
+
+        // Print the Meter
+        print(FORMAT_METER, operationStr, meter.getCount(),
+              meter.getMeanRate() * rateFactor,
+              meter.getOneMinuteRate() * rateFactor,
+              meter.getFiveMinuteRate() * rateFactor,
+              meter.getFifteenMinuteRate() * rateFactor);
+    }
 }