JSON Serializers for Intents

Implements JSON serializers for objects
used by the Intent REST APIs:
- ShortestPathIntent
- LinkEvent
- SwitchPort

Change-Id: I7dfe98d7b9d0d2449af02b1e1f9361a105a7efa5
diff --git a/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java b/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java
index 92f57ad..5c4e961 100644
--- a/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java
+++ b/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java
@@ -1,11 +1,14 @@
 package net.onrc.onos.core.intent;
 
 import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.topology.web.serializers.ShortestPathIntentSerializer;
 import net.onrc.onos.core.util.Dpid;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
 
 /**
  * @author Toshio Koide (t-koide@onlab.us)
  */
+@JsonSerialize(using = ShortestPathIntentSerializer.class)
 public class ShortestPathIntent extends Intent {
     public static final long EMPTYMACADDRESS = 0;
     public static final int EMPTYIPADDRESS = 0;
diff --git a/src/main/java/net/onrc/onos/core/topology/LinkEvent.java b/src/main/java/net/onrc/onos/core/topology/LinkEvent.java
index 40d7400..9b6630f 100644
--- a/src/main/java/net/onrc/onos/core/topology/LinkEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/LinkEvent.java
@@ -3,12 +3,16 @@
 import java.nio.ByteBuffer;
 
 import net.onrc.onos.core.topology.PortEvent.SwitchPort;
+import net.onrc.onos.core.topology.web.serializers.LinkEventSerializer;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
 
 /**
  * Self-contained Link event Object.
  * <p/>
  * TODO: We probably want common base class/interface for Self-Contained Event Object.
  */
+
+@JsonSerialize(using = LinkEventSerializer.class)
 public class LinkEvent {
     protected final SwitchPort src;
     protected final SwitchPort dst;
diff --git a/src/main/java/net/onrc/onos/core/topology/PortEvent.java b/src/main/java/net/onrc/onos/core/topology/PortEvent.java
index 1f20f22..39af378 100644
--- a/src/main/java/net/onrc/onos/core/topology/PortEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/PortEvent.java
@@ -1,5 +1,8 @@
 package net.onrc.onos.core.topology;
 
+import net.onrc.onos.core.topology.web.serializers.SwitchPortSerializer;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
 import java.nio.ByteBuffer;
 
 /**
@@ -8,6 +11,7 @@
  * TODO: We probably want common base class/interface for Self-Contained Event Object.
  */
 public class PortEvent {
+    @JsonSerialize(using = SwitchPortSerializer.class)
     public static class SwitchPort {
         public final Long dpid;
         public final Long number;
diff --git a/src/main/java/net/onrc/onos/core/topology/web/serializers/LinkEventSerializer.java b/src/main/java/net/onrc/onos/core/topology/web/serializers/LinkEventSerializer.java
new file mode 100644
index 0000000..2621167
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/web/serializers/LinkEventSerializer.java
@@ -0,0 +1,43 @@
+package net.onrc.onos.core.topology.web.serializers;
+
+import net.onrc.onos.core.topology.LinkEvent;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+
+import java.io.IOException;
+
+/**
+ * JSON serializer for LinkEvents.
+ */
+public class LinkEventSerializer extends SerializerBase<LinkEvent> {
+
+    /**
+     * Public constructor - just calls its super class constructor.
+     */
+    public LinkEventSerializer() {
+        super(LinkEvent.class);
+    }
+
+    /**
+     * Serializes a LinkEvent object.
+     *
+     * @param linkEvent LinkEvent to serialize
+     * @param jsonGenerator generator to add the serialized object to
+     * @param serializerProvider not used
+     * @throws IOException if the JSON serialization fails
+     */
+    @Override
+    public void serialize(final LinkEvent linkEvent,
+                          final JsonGenerator jsonGenerator,
+                          final SerializerProvider serializerProvider)
+            throws IOException {
+
+        jsonGenerator.writeStartObject();
+
+        jsonGenerator.writeObjectField("src", linkEvent.getSrc());
+        jsonGenerator.writeObjectField("dst", linkEvent.getDst());
+
+        jsonGenerator.writeEndObject();
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/topology/web/serializers/ShortestPathIntentSerializer.java b/src/main/java/net/onrc/onos/core/topology/web/serializers/ShortestPathIntentSerializer.java
new file mode 100644
index 0000000..f0fa00c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/web/serializers/ShortestPathIntentSerializer.java
@@ -0,0 +1,90 @@
+package net.onrc.onos.core.topology.web.serializers;
+
+
+import com.google.common.net.InetAddresses;
+import net.onrc.onos.core.intent.ShortestPathIntent;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+import org.openflow.util.HexString;
+
+import java.io.IOException;
+
+/**
+ * JSON serializer for ShortestPathIntents.
+ */
+public class ShortestPathIntentSerializer extends SerializerBase<ShortestPathIntent> {
+
+    /**
+     * Public constructor - just calls its super class constructor.
+     */
+    public ShortestPathIntentSerializer() {
+        super(ShortestPathIntent.class);
+    }
+
+    /**
+     * Converts an integer into a string representing an IP address.
+     *
+     * @param ipAddress integer representation of the address
+     * @return string that represents the address
+     */
+    private String toIPAddressString(final int ipAddress) {
+        return InetAddresses.fromInteger(ipAddress).getHostAddress();
+    }
+
+    /**
+     * Serializes a ShortestPathIntent object.
+     *
+     * @param intent Intent to serialize
+     * @param jsonGenerator generator to add the serialized object to
+     * @param serializerProvider not used
+     * @throws IOException if the JSON serialization fails
+     */
+    @Override
+    public void serialize(final ShortestPathIntent intent,
+                          final JsonGenerator jsonGenerator,
+                          final SerializerProvider serializerProvider)
+           throws IOException {
+        jsonGenerator.writeStartObject();
+
+        jsonGenerator.writeStringField("id", intent.getId());
+        jsonGenerator.writeStringField("state", intent.getState().toString());
+        jsonGenerator.writeStringField("pathFrozen",
+                                       Boolean.toString(intent.isPathFrozen()));
+
+        jsonGenerator.writeStringField("srcSwitchDpid",
+                                       HexString.toHexString(intent.getSrcSwitchDpid()));
+        jsonGenerator.writeStringField("srcPortNumber",
+                                       Long.toString(intent.getSrcPortNumber()));
+        jsonGenerator.writeStringField("srcMac",
+                                       HexString.toHexString(intent.getSrcMac()));
+        jsonGenerator.writeStringField("srcIp",
+                                       toIPAddressString(intent.getSrcIp()));
+
+        jsonGenerator.writeStringField("dstSwitchDpid",
+                HexString.toHexString(intent.getDstSwitchDpid()));
+        jsonGenerator.writeStringField("dstPortNumber",
+                                       Long.toString(intent.getDstPortNumber()));
+        jsonGenerator.writeStringField("dstMac",
+                                       HexString.toHexString(intent.getDstMac()));
+        jsonGenerator.writeStringField("dstIp",
+                                       toIPAddressString(intent.getDstIp()));
+
+        jsonGenerator.writeStringField("idleTimeout",
+                                       Integer.toString(intent.getIdleTimeout()));
+        jsonGenerator.writeStringField("hardTimeout",
+                                       Integer.toString(intent.getHardTimeout()));
+        jsonGenerator.writeStringField("firstSwitchIdleTimeout",
+                                       Integer.toString(intent.getFirstSwitchIdleTimeout()));
+        jsonGenerator.writeStringField("firstSwitchHardTimeout",
+                                       Integer.toString(intent.getFirstSwitchHardTimeout()));
+
+        jsonGenerator.writeArrayFieldStart("logs");
+        for (final String log : intent.getLogs()) {
+            jsonGenerator.writeObject(log);
+        }
+        jsonGenerator.writeEndArray();
+
+        jsonGenerator.writeEndObject();
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/topology/web/serializers/SwitchPortSerializer.java b/src/main/java/net/onrc/onos/core/topology/web/serializers/SwitchPortSerializer.java
new file mode 100644
index 0000000..c9820a5
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/web/serializers/SwitchPortSerializer.java
@@ -0,0 +1,46 @@
+package net.onrc.onos.core.topology.web.serializers;
+
+import net.onrc.onos.core.topology.PortEvent.SwitchPort;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+import org.openflow.util.HexString;
+
+import java.io.IOException;
+
+/**
+ * JSON Serializer for SwitchPorts.
+ */
+public class SwitchPortSerializer extends SerializerBase<SwitchPort> {
+
+    /**
+     * Public constructor - just calls its super class constructor.
+     */
+    public SwitchPortSerializer() {
+        super(SwitchPort.class);
+    }
+
+    /**
+     * Serializes a SwitchPort object.
+     *
+     * @param switchPort object to serialize
+     * @param jsonGenerator generator to add the serialized object to
+     * @param serializerProvider not used
+     * @throws IOException if the serialization fails
+     */
+    @Override
+    public void serialize(final SwitchPort switchPort,
+                          final JsonGenerator jsonGenerator,
+                          final SerializerProvider serializerProvider)
+            throws IOException {
+        jsonGenerator.writeStartObject();
+
+        jsonGenerator.writeStringField("dpid",
+                                       HexString.toHexString(switchPort.getDpid()));
+        jsonGenerator.writeStringField("portNumber",
+                                       Long.toString(switchPort.getNumber()));
+
+        jsonGenerator.writeEndObject();
+    }
+
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestIntentHighGet.java b/src/test/java/net/onrc/onos/api/rest/TestRestIntentHighGet.java
index 1dde7bb..97003aa 100644
--- a/src/test/java/net/onrc/onos/api/rest/TestRestIntentHighGet.java
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestIntentHighGet.java
@@ -1,6 +1,7 @@
 package net.onrc.onos.api.rest;
 
 
+import com.google.common.net.InetAddresses;
 import net.onrc.onos.core.intent.IntentOperation;
 import net.onrc.onos.core.intent.IntentOperationList;
 import net.onrc.onos.core.intent.ShortestPathIntent;
@@ -35,7 +36,9 @@
 public class TestRestIntentHighGet extends TestRestIntent {
     private static final Long LOCAL_PORT = 0xFFFEL;
     private static final String BAD_SWITCH_INTENT_NAME = "No Such Switch Intent";
-
+    private static final String IP_ADDRESS_1 = "127.0.0.1";
+    private static final String IP_ADDRESS_2 = "127.0.0.2";
+    private static final String IP_ADDRESS_3 = "127.0.0.3";
 
     /**
      * Create the web server, PathCalcRuntime, and mocks required for
@@ -62,17 +65,24 @@
      * Make a set of Intents that can be used as test data.
      */
     private void makeDefaultIntents() {
+        final int ipAddress1AsInt = InetAddresses.coerceToInteger(
+                InetAddresses.forString(IP_ADDRESS_1));
+        final int ipAddress2AsInt = InetAddresses.coerceToInteger(
+                InetAddresses.forString(IP_ADDRESS_2));
+        final int ipAddress3AsInt = InetAddresses.coerceToInteger(
+                InetAddresses.forString(IP_ADDRESS_3));
+
         // create shortest path intents
         final IntentOperationList opList = new IntentOperationList();
         opList.add(IntentOperation.Operator.ADD,
                 new ShortestPathIntent(BAD_SWITCH_INTENT_NAME, 111L, 12L,
                         LOCAL_PORT, 2L, 21L, LOCAL_PORT));
         opList.add(IntentOperation.Operator.ADD,
-                new ShortestPathIntent("1:2", 1L, 14L, LOCAL_PORT, 4L, 41L,
-                        LOCAL_PORT));
+                new ShortestPathIntent("1:2", 1L, 14L, LOCAL_PORT, ipAddress1AsInt,
+                                       4L, 41L, LOCAL_PORT, ipAddress2AsInt));
         opList.add(IntentOperation.Operator.ADD,
-                new ShortestPathIntent("1:3", 2L, 23L, LOCAL_PORT, 3L, 32L,
-                        LOCAL_PORT));
+                new ShortestPathIntent("1:3", 2L, 23L, LOCAL_PORT, ipAddress2AsInt,
+                                       3L, 32L, LOCAL_PORT, ipAddress3AsInt));
 
         // compile high-level intent operations into low-level intent
         // operations (calculate paths)
@@ -169,5 +179,23 @@
         assertThat(intent.get("id"), is(equalTo("1:2")));
         assertThat(intent, hasKey("state"));
         assertThat(intent.get("state"), is(equalTo("INST_REQ")));
+
+        assertThat(intent, hasKey("srcSwitchDpid"));
+        assertThat(intent.get("srcSwitchDpid"), is(equalTo("00:00:00:00:00:00:00:01")));
+        assertThat(intent, hasKey("srcPortNumber"));
+        assertThat(intent.get("srcPortNumber"), is(equalTo("14")));
+        assertThat(intent, hasKey("srcMac"));
+        assertThat(intent.get("srcMac"), is(equalTo("00:00:00:00:00:00:ff:fe")));
+        assertThat(intent, hasKey("srcMac"));
+        assertThat(intent.get("srcIp"), is(equalTo(IP_ADDRESS_1)));
+
+        assertThat(intent, hasKey("dstSwitchDpid"));
+        assertThat(intent.get("dstSwitchDpid"), is(equalTo("00:00:00:00:00:00:00:04")));
+        assertThat(intent, hasKey("dstPortNumber"));
+        assertThat(intent.get("dstPortNumber"), is(equalTo("41")));
+        assertThat(intent, hasKey("dstMac"));
+        assertThat(intent.get("dstMac"), is(equalTo("00:00:00:00:00:00:ff:fe")));
+        assertThat(intent, hasKey("dstMac"));
+        assertThat(intent.get("dstIp"), is(equalTo(IP_ADDRESS_2)));
     }
 }