[ONOS-4530] Allow to specify appId when insert FlowRule through REST

- Augment FlowRuleCodec to encode FlowRule
- Add unit test for encode method of FlowRuleCodec
- Add getFlowByAppId and removeFlowByAppId methods in FlowsWebResource
- Add more unit tests for FlowWebResource
- Add FlowRules.json swagger doc
- Rename Flows.json to FlowEntries.json, correct FlowEntries.json

Change-Id: Ic3ec390c13a349e51ae4208adbc478564b6724ba
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
index e36fe89f..1a41e5c 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
@@ -19,6 +19,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.DefaultFlowRule;
@@ -26,6 +27,7 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onlab.util.Tools.nullIsIllegal;
 
 /**
@@ -36,14 +38,46 @@
     private static final String PRIORITY = "priority";
     private static final String TIMEOUT = "timeout";
     private static final String IS_PERMANENT = "isPermanent";
+    private static final String APP_ID = "appId";
     private static final String TABLE_ID = "tableId";
     private static final String DEVICE_ID = "deviceId";
     private static final String TREATMENT = "treatment";
     private static final String SELECTOR = "selector";
     private static final String MISSING_MEMBER_MESSAGE =
-            " member is required in FlowRule";
+                                " member is required in FlowRule";
     public static final String REST_APP_ID = "org.onosproject.rest";
 
+    @Override
+    public ObjectNode encode(FlowRule flowRule, CodecContext context) {
+        checkNotNull(flowRule, "Flow rule cannot be null");
+
+        CoreService service = context.getService(CoreService.class);
+        ApplicationId appId = service.getAppId(flowRule.appId());
+        String strAppId = (appId == null) ? "<none>" : appId.name();
+
+        final ObjectNode result = context.mapper().createObjectNode()
+                .put("id", Long.toString(flowRule.id().value()))
+                .put("tableId", flowRule.tableId())
+                .put("appId", strAppId)
+                .put("priority", flowRule.priority())
+                .put("timeout", flowRule.timeout())
+                .put("isPermanent", flowRule.isPermanent())
+                .put("deviceId", flowRule.deviceId().toString());
+
+        if (flowRule.treatment() != null) {
+            final JsonCodec<TrafficTreatment> treatmentCodec =
+                    context.codec(TrafficTreatment.class);
+            result.set("treatment", treatmentCodec.encode(flowRule.treatment(), context));
+        }
+
+        if (flowRule.selector() != null) {
+            final JsonCodec<TrafficSelector> selectorCodec =
+                    context.codec(TrafficSelector.class);
+            result.set("selector", selectorCodec.encode(flowRule.selector(), context));
+        }
+
+        return result;
+    }
 
     @Override
     public FlowRule decode(ObjectNode json, CodecContext context) {
@@ -54,8 +88,9 @@
         FlowRule.Builder resultBuilder = new DefaultFlowRule.Builder();
 
         CoreService coreService = context.getService(CoreService.class);
-        resultBuilder.fromApp(coreService
-                .registerApplication(REST_APP_ID));
+        JsonNode appIdJson = json.get(APP_ID);
+        String appId = appIdJson != null ? appIdJson.asText() : REST_APP_ID;
+        resultBuilder.fromApp(coreService.registerApplication(appId));
 
         int priority = nullIsIllegal(json.get(PRIORITY),
                 PRIORITY + MISSING_MEMBER_MESSAGE).asInt();
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java
index 9c5e3fd..11b7eb6 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java
@@ -17,6 +17,8 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.EthType;
@@ -29,12 +31,14 @@
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.GridType;
 import org.onosproject.net.Lambda;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OchSignalType;
 import org.onosproject.net.OduSignalType;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.EthCriterion;
@@ -75,6 +79,7 @@
 import java.util.SortedMap;
 import java.util.TreeMap;
 
+import static org.easymock.EasyMock.anyShort;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
@@ -105,6 +110,7 @@
 
         expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
                 .andReturn(APP_ID).anyTimes();
+        expect(mockCoreService.getAppId(anyShort())).andReturn(APP_ID).anyTimes();
         replay(mockCoreService);
         context.registerService(CoreService.class, mockCoreService);
     }
@@ -166,6 +172,118 @@
     SortedMap<String, Instruction> instructions = new TreeMap<>();
 
     /**
+     * Checks that a simple rule encodes properly.
+     */
+    @Test
+    public void testFlowRuleEncode() {
+
+        DeviceId deviceId = DeviceId.deviceId("of:000000000000000a");
+        FlowRule permFlowRule = DefaultFlowRule.builder()
+                                                    .withCookie(1)
+                                                    .forTable(1)
+                                                    .withPriority(1)
+                                                    .makePermanent()
+                                                    .forDevice(deviceId).build();
+
+        FlowRule tempFlowRule =  DefaultFlowRule.builder()
+                                                .withCookie(1)
+                                                .forTable(1)
+                                                .withPriority(1)
+                                                .makeTemporary(1000)
+                                                .forDevice(deviceId).build();
+
+        ObjectNode permFlowRuleJson = flowRuleCodec.encode(permFlowRule, context);
+        ObjectNode tempFlowRuleJson = flowRuleCodec.encode(tempFlowRule, context);
+
+        assertThat(permFlowRuleJson, FlowRuleJsonMatcher.matchesFlowRule(permFlowRule));
+        assertThat(tempFlowRuleJson, FlowRuleJsonMatcher.matchesFlowRule(tempFlowRule));
+    }
+
+    private static final class FlowRuleJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+        private final FlowRule flowRule;
+
+        private FlowRuleJsonMatcher(FlowRule flowRule) {
+            this.flowRule = flowRule;
+        }
+
+        @Override
+        protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+            // check id
+            long jsonId = jsonNode.get("id").asLong();
+            long id = flowRule.id().id();
+            if (jsonId != id) {
+                description.appendText("flow rule id was " + jsonId);
+                return false;
+            }
+
+            // TODO: need to check application ID
+
+            // check tableId
+            int jsonTableId = jsonNode.get("tableId").asInt();
+            int tableId = flowRule.tableId();
+            if (jsonTableId != tableId) {
+                description.appendText("table id was " + jsonId);
+                return false;
+            }
+
+            // check priority
+            int jsonPriority = jsonNode.get("priority").asInt();
+            int priority = flowRule.priority();
+            if (jsonPriority != priority) {
+                description.appendText("priority was " + jsonPriority);
+                return false;
+            }
+
+            // check timeout
+            int jsonTimeout = jsonNode.get("timeout").asInt();
+            int timeout = flowRule.timeout();
+            if (jsonTimeout != timeout) {
+                description.appendText("timeout was " + jsonTimeout);
+                return false;
+            }
+
+            // check isPermanent
+            boolean jsonIsPermanent = jsonNode.get("isPermanent").asBoolean();
+            boolean isPermanent = flowRule.isPermanent();
+            if (jsonIsPermanent != isPermanent) {
+                description.appendText("isPermanent was " + jsonIsPermanent);
+                return false;
+            }
+
+            // check deviceId
+            String jsonDeviceId = jsonNode.get("deviceId").asText();
+            String deviceId = flowRule.deviceId().toString();
+            if (!jsonDeviceId.equals(deviceId)) {
+                description.appendText("deviceId was " + jsonDeviceId);
+                return false;
+            }
+
+            // TODO: need to check traffic treatment
+
+            // TODO: need to check selector
+
+            return true;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText(flowRule.toString());
+        }
+
+        /**
+         * Factory to allocate a flow rule matcher.
+         *
+         * @param flowRule flow rule object we are looking for
+         * @return matcher
+         */
+        public static FlowRuleJsonMatcher matchesFlowRule(FlowRule flowRule) {
+            return new FlowRuleJsonMatcher(flowRule);
+        }
+    }
+
+    /**
      * Looks up an instruction in the instruction map based on type and subtype.
      *
      * @param type type string