Added a new module: ONOS intent metrics application

It can be used as an Intent-related event and event metrics collector.
It can be loaded by one of the following two (new) features:
     onos-app-metrics, onos-app-metrics-intent

After loading the module, it subscribes for intent-related events
and keeps the following state:
 (a) The last 100 events
 (b) The timestamp of the last event (ms after epoch) as observed by this
     module for each event type: SUBMITTED, INSTALLED, WITHDRAWN.
     The missing event type is the equivalent of "Withdraw Requested"
 (c) The rate of each intent event type: count, median rate, average rate
      over the last 1, 5 or 15 minutes

The following CLI commands are added:
 * onos:intents-events
   Shows the last 100 intent events
 * onos:intents-events-metrics
   Shows the timestamp of the last event (ms after epoch) as observed by
   this module for each event type, and the rate of the topology
   events (for each event type): see (b) and (c) above

Change-Id: I9f23e9086bbd433b8f24283539abdeb97e199e2e
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsListCommand.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsListCommand.java
new file mode 100644
index 0000000..e07c3a5
--- /dev/null
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsListCommand.java
@@ -0,0 +1,68 @@
+package org.onlab.onos.metrics.intent.cli;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.metrics.intent.IntentMetricsService;
+import org.onlab.onos.net.intent.IntentEvent;
+
+/**
+ * Command to show the list of last intent events.
+ */
+@Command(scope = "onos", name = "intents-events",
+         description = "Lists the last intent events")
+public class IntentEventsListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT_EVENT = "Event=%s";
+
+    @Override
+    protected void execute() {
+        IntentMetricsService service = get(IntentMetricsService.class);
+
+        if (outputJson()) {
+            print("%s", json(service.getEvents()));
+        } else {
+            for (IntentEvent event : service.getEvents()) {
+                print(FORMAT_EVENT, event);
+                print("");          // Extra empty line for clarity
+            }
+        }
+    }
+
+    /**
+     * Produces a JSON array of intent events.
+     *
+     * @param intentEvents the intent events with the data
+     * @return JSON array with the intent events
+     */
+    private JsonNode json(List<IntentEvent> intentEvents) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
+
+        for (IntentEvent event : intentEvents) {
+            result.add(json(mapper, event));
+        }
+        return result;
+    }
+
+    /**
+     * Produces JSON object for a intent event.
+     *
+     * @param mapper the JSON object mapper to use
+     * @param intentEvent the intent event with the data
+     * @return JSON object for the intent event
+     */
+    private ObjectNode json(ObjectMapper mapper, IntentEvent intentEvent) {
+        ObjectNode result = mapper.createObjectNode();
+
+        result.put("time", intentEvent.time())
+            .put("type", intentEvent.type().toString())
+            .put("event", intentEvent.toString());
+        return result;
+    }
+}