[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
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
index fc99c49..42e9590 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
@@ -21,6 +21,8 @@
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ListMultimap;
 import org.onlab.util.ItemNotFoundException;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
@@ -36,6 +38,7 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -61,6 +64,7 @@
 
     private static final String DEVICE_NOT_FOUND = "Device is not found";
     private static final String FLOW_NOT_FOUND = "Flow is not found";
+    private static final String APP_ID_NOT_FOUND = "Application Id is not found";
     private static final String FLOWS = "flows";
     private static final String DEVICE_ID = "deviceId";
     private static final String FLOW_ID = "flowId";
@@ -73,7 +77,7 @@
      * Gets all flow entries. Returns array of all flow rules in the system.
      *
      * @return 200 OK with a collection of flows
-     * @onos.rsModel Flows
+     * @onos.rsModel FlowEntries
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -107,10 +111,15 @@
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response createFlows(InputStream stream) {
+    public Response createFlows(@QueryParam("appId") String appId, InputStream stream) {
         try {
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
             ArrayNode flowsArray = (ArrayNode) jsonTree.get(FLOWS);
+
+            if (appId != null) {
+                flowsArray.forEach(flowJson -> ((ObjectNode) flowJson).put("appId", appId));
+            }
+
             List<FlowRule> rules = codec(FlowRule.class).decode(flowsArray, this);
             service.applyFlowRules(rules.toArray(new FlowRule[rules.size()]));
             rules.forEach(flowRule -> {
@@ -131,10 +140,11 @@
      *
      * @param deviceId device identifier
      * @return 200 OK with a collection of flows of given device
-     * @onos.rsModel Flows
+     * @onos.rsModel FlowEntries
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
+    // TODO: we need to add "/device" suffix to the path to differentiate with appId
     @Path("{deviceId}")
     public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) {
         final Iterable<FlowEntry> flowEntries =
@@ -150,13 +160,13 @@
     }
 
     /**
-     * Gets flow rule. Returns the flow entry specified by the device id and
+     * Gets flow rules. Returns the flow entry specified by the device id and
      * flow rule id.
      *
      * @param deviceId device identifier
      * @param flowId   flow rule identifier
-     * @return 200 OK with a flows of given device and flow
-     * @onos.rsModel Flows
+     * @return 200 OK with a collection of flows of given device and flow
+     * @onos.rsModel FlowEntries
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -178,6 +188,43 @@
     }
 
     /**
+     * Gets flow rules generated by an application.
+     * Returns the flow rule specified by the application id.
+     *
+     * @param appId application identifier
+     * @return 200 OK with a collection of flows of given application id
+     * @onos.rsModel FlowRules
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("application/{appId}")
+    public Response getFlowByAppId(@PathParam("appId") String appId) {
+        final ApplicationService appService = get(ApplicationService.class);
+        final ApplicationId idInstant = nullIsNotFound(appService.getId(appId), APP_ID_NOT_FOUND);
+        final Iterable<FlowRule> flowRules = service.getFlowRulesById(idInstant);
+
+        flowRules.forEach(flow -> flowsNode.add(codec(FlowRule.class).encode(flow, this)));
+        return ok(root).build();
+    }
+
+    /**
+     * Removes flow rules by application ID.
+     * Removes a collection of flow rules generated by the given application.
+     *
+     * @param appId application identifier
+     * @return 204 NO CONTENT
+     */
+    @DELETE
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("application/{appId}")
+    public Response removeFlowByAppId(@PathParam("appId") String appId) {
+        final ApplicationService appService = get(ApplicationService.class);
+        final ApplicationId idInstant = nullIsNotFound(appService.getId(appId), APP_ID_NOT_FOUND);
+        service.removeFlowRulesById(idInstant);
+        return Response.noContent().build();
+    }
+
+    /**
      * Creates new flow rule. Creates and installs a new flow rule for the
      * specified device. <br>
      * Instructions description:
@@ -187,6 +234,7 @@
      * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Criteria
      *
      * @param deviceId device identifier
+     * @param appId    application identifier
      * @param stream   flow rule JSON
      * @return status of the request - CREATED if the JSON is correct,
      * BAD_REQUEST if the JSON is invalid
@@ -197,6 +245,7 @@
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createFlow(@PathParam("deviceId") String deviceId,
+                               @QueryParam("appId") String appId,
                                InputStream stream) {
         try {
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
@@ -207,6 +256,11 @@
                         "Invalid deviceId in flow creation request");
             }
             jsonTree.put("deviceId", deviceId);
+
+            if (appId != null) {
+                jsonTree.put("appId", appId);
+            }
+
             FlowRule rule = codec(FlowRule.class).decode(jsonTree, this);
             service.applyFlowRules(rule);
             UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
@@ -223,7 +277,7 @@
     }
 
     /**
-     * Remove flow rule. Removes the specified flow rule.
+     * Removes flow rule. Removes the specified flow rule.
      *
      * @param deviceId device identifier
      * @param flowId   flow rule identifier
diff --git a/web/api/src/main/resources/definitions/FlowEntries.json b/web/api/src/main/resources/definitions/FlowEntries.json
new file mode 100644
index 0000000..15f8628
--- /dev/null
+++ b/web/api/src/main/resources/definitions/FlowEntries.json
@@ -0,0 +1,373 @@
+{
+  "type": "object",
+  "title": "flows",
+  "required": [
+    "flows"
+  ],
+  "properties": {
+    "flows": {
+      "type": "array",
+      "xml": {
+        "name": "flows",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "title": "flow",
+        "required": [
+          "id",
+          "tableId",
+          "appId",
+          "groupId",
+          "priority",
+          "timeout",
+          "isPermanent",
+          "deviceId",
+          "state",
+          "life",
+          "packets",
+          "bytes",
+          "lastSeen"
+        ],
+        "properties": {
+          "id": {
+            "type": "string",
+            "example": "12103425214920339"
+          },
+          "tableId": {
+            "type": "integer",
+            "format": "int32",
+            "example": 3
+          },
+          "appId": {
+            "type": "string",
+            "example": "org.onosproject.core"
+          },
+          "groupId": {
+            "type": "integer",
+            "format": "int64",
+            "example": 0
+          },
+          "priority": {
+            "type": "integer",
+            "format": "int32",
+            "example": 40000
+          },
+          "timeout": {
+            "type": "integer",
+            "format": "int32",
+            "example": 0
+          },
+          "isPermanent": {
+            "type": "boolean",
+            "example": true
+          },
+          "deviceId": {
+            "type": "string",
+            "example": "of:0000000000000003"
+          },
+          "state": {
+            "type": "string",
+            "example": "ADDED"
+          },
+          "life": {
+            "type": "integer",
+            "format": "int64",
+            "example": 69889
+          },
+          "packets": {
+            "type": "integer",
+            "format": "int64",
+            "example": 22546
+          },
+          "bytes": {
+            "type": "integer",
+            "format": "int64",
+            "example": 1826226
+          },
+          "lastSeen": {
+            "type": "integer",
+            "format": "int64",
+            "example": 1447892365670
+          },
+          "treatment": {
+            "type": "object",
+            "title": "treatment",
+            "required": [
+              "instructions",
+              "deferred"
+            ],
+            "properties": {
+              "instructions": {
+                "type": "array",
+                "title": "treatment",
+                "required": [
+                  "properties",
+                  "port"
+                ],
+                "items": {
+                  "type": "object",
+                  "title": "instruction",
+                  "required": [
+                    "type",
+                    "port"
+                  ],
+                  "properties": {
+                    "type": {
+                      "type": "string",
+                      "example": "OUTPUT"
+                    },
+                    "port": {
+                      "type": "string",
+                      "example": "CONTROLLER"
+                    }
+                  }
+                }
+              },
+              "deferred": {
+                "type": "array",
+                "xml": {
+                  "name": "deferred",
+                  "wrapped": true
+                },
+                "items": {
+                  "type": "string"
+                }
+              }
+            }
+          },
+          "selector": {
+            "type": "object",
+            "title": "selector",
+            "required": [
+              "criteria"
+            ],
+            "properties": {
+              "criteria": {
+                "type": "array",
+                "xml": {
+                  "name": "criteria",
+                  "wrapped": true
+                },
+                "items": {
+                  "type": "object",
+                  "title": "criteria",
+                  "properties": {
+                    "type": {
+                      "type": "string",
+                      "description": "Ethernet field name",
+                      "example": "ETH_TYPE"
+                    },
+                    "ethType": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": "0x88cc",
+                      "description": "Ethernet frame type"
+                    },
+                    "mac": {
+                      "type": "string",
+                      "example": "00:00:11:00:00:01"
+                    },
+                    "port": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "Match port"
+                    },
+                    "metadata": {
+                      "type": "Hex16",
+                      "format": "Hex16",
+                      "example": "0xabcdL",
+                      "description": "Metadata passed between tables"
+                    },
+                    "vlanId": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": "0x1000"
+                    },
+                    "priority": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "VLAN priority."
+                    },
+                    "ipDscp": {
+                      "type": "byte",
+                      "format": "byte",
+                      "description": "IP DSCP (6 bits in ToS field)"
+                    },
+                    "ipEcn": {
+                      "type": "byte",
+                      "format": "byte",
+                      "description": "IP ECN (2 bits in ToS field)."
+                    },
+                    "protocol": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "IP protocol"
+                    },
+                    "ip": {
+                      "type": "string",
+                      "example": "10.1.1.0/24",
+                      "description": "IP source address"
+                    },
+                    "tcpPort": {
+                      "type": "integer",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "TCP source address"
+                    },
+                    "udpPort": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "UDP source address"
+                    },
+                    "sctpPort": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "SCTP source address"
+                    },
+                    "icmpType": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
+                    },
+                    "icmpCode": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
+                    },
+                    "flowLabel": {
+                      "type": "Hex16",
+                      "format": "Hex16",
+                      "example": "0xffffe",
+                      "description": "IPv6 Flow Label (RFC 6437)"
+                    },
+                    "icmpv6Type": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV6 type (RFC2463)"
+                    },
+                    "icmpv6Code": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV6 code (RFC2463)"
+                    },
+                    "targetAddress": {
+                      "type": "String",
+                      "example": "10.1.1.0/24",
+                      "description": "IPv6 Neighbor discovery target address"
+                    },
+                    "label": {
+                      "type": "int32",
+                      "format": "int32",
+                      "example": 1,
+                      "description": "MPLS label"
+                    },
+                    "exthdrFlags": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "IPv6 extension header pseudo-field"
+                    },
+                    "lambda": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "wavelength abstraction"
+                    },
+                    "gridType": {
+                      "type": "String",
+                      "example": "DWDM",
+                      "description": "Type of wavelength grid"
+                    },
+                    "channelSpacing": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 100,
+                      "description": "Optical channel spacing"
+                    },
+                    "spacingMultiplier": {
+                      "type": "integer",
+                      "format": "int64",
+                      "example": 4,
+                      "description": "Optical channel spacing multiplier"
+                    },
+                    "slotGranularity": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 8
+                    },
+                    "ochSignalId": {
+                      "type": "integer",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "Optical channel signal ID"
+                    },
+                    "tunnelId": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 5,
+                      "description": "Tunnel ID"
+                    },
+                    "ochSignalType": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "Optical channel signal type"
+                    },
+                    "oduSignalId": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "ODU (Optical channel Data Unit) signal ID."
+                    },
+                    "tributaryPortNumber": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 11,
+                      "description": "OPU (Optical channel Payload Unit) port number."
+                    },
+                    "tributarySlotLen": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 80,
+                      "description": "OPU (Optical channel Payload Unit) slot length."
+                    },
+                    "tributarySlotBitmap": {
+                      "type": "array",
+                      "title": "tributarySlotBitmap",
+                      "description": "OPU (Optical channel Payload Unit) slot bitmap.",
+                      "required": [
+                        "byte",
+                        "port"
+                      ],
+                      "items": {
+                        "type": "byte",
+                        "title": "byte",
+                        "example": 1
+                      }
+                    },
+                    "oduSignalType": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 4,
+                      "description": "ODU (Optical channel Data Unit) signal type."
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/FlowRules.json b/web/api/src/main/resources/definitions/FlowRules.json
new file mode 100644
index 0000000..026b726
--- /dev/null
+++ b/web/api/src/main/resources/definitions/FlowRules.json
@@ -0,0 +1,344 @@
+{
+  "type": "object",
+  "title": "flows",
+  "required": [
+    "flows"
+  ],
+  "properties": {
+    "flows": {
+      "type": "array",
+      "xml": {
+        "name": "flows",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "title": "flow",
+        "required": [
+          "id",
+          "tableId",
+          "appId",
+          "groupId",
+          "priority",
+          "timeout",
+          "isPermanent",
+          "deviceId"
+        ],
+        "properties": {
+          "id": {
+            "type": "string",
+            "example": "12103425214920339"
+          },
+          "tableId": {
+            "type": "integer",
+            "format": "int32",
+            "example": 3
+          },
+          "appId": {
+            "type": "string",
+            "example": "org.onosproject.core"
+          },
+          "groupId": {
+            "type": "integer",
+            "format": "int64",
+            "example": 0
+          },
+          "priority": {
+            "type": "integer",
+            "format": "int32",
+            "example": 40000
+          },
+          "timeout": {
+            "type": "integer",
+            "format": "int32",
+            "example": 0
+          },
+          "isPermanent": {
+            "type": "boolean",
+            "example": true
+          },
+          "deviceId": {
+            "type": "string",
+            "example": "of:0000000000000003"
+          },
+          "treatment": {
+            "type": "object",
+            "title": "treatment",
+            "required": [
+              "instructions",
+              "deferred"
+            ],
+            "properties": {
+              "instructions": {
+                "type": "array",
+                "title": "treatment",
+                "required": [
+                  "properties",
+                  "port"
+                ],
+                "items": {
+                  "type": "object",
+                  "title": "instruction",
+                  "required": [
+                    "type",
+                    "port"
+                  ],
+                  "properties": {
+                    "type": {
+                      "type": "string",
+                      "example": "OUTPUT"
+                    },
+                    "port": {
+                      "type": "string",
+                      "example": "CONTROLLER"
+                    }
+                  }
+                }
+              },
+              "deferred": {
+                "type": "array",
+                "xml": {
+                  "name": "deferred",
+                  "wrapped": true
+                },
+                "items": {
+                  "type": "string"
+                }
+              }
+            }
+          },
+          "selector": {
+            "type": "object",
+            "title": "selector",
+            "required": [
+              "criteria"
+            ],
+            "properties": {
+              "criteria": {
+                "type": "array",
+                "xml": {
+                  "name": "criteria",
+                  "wrapped": true
+                },
+                "items": {
+                  "type": "object",
+                  "title": "criteria",
+                  "properties": {
+                    "type": {
+                      "type": "string",
+                      "description": "Ethernet field name",
+                      "example": "ETH_TYPE"
+                    },
+                    "ethType": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": "0x88cc",
+                      "description": "Ethernet frame type"
+                    },
+                    "mac": {
+                      "type": "string",
+                      "example": "00:00:11:00:00:01"
+                    },
+                    "port": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "Match port"
+                    },
+                    "metadata": {
+                      "type": "Hex16",
+                      "format": "Hex16",
+                      "example": "0xabcdL",
+                      "description": "Metadata passed between tables"
+                    },
+                    "vlanId": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": "0x1000"
+                    },
+                    "priority": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "VLAN priority."
+                    },
+                    "ipDscp": {
+                      "type": "byte",
+                      "format": "byte",
+                      "description": "IP DSCP (6 bits in ToS field)"
+                    },
+                    "ipEcn": {
+                      "type": "byte",
+                      "format": "byte",
+                      "description": "IP ECN (2 bits in ToS field)."
+                    },
+                    "protocol": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "IP protocol"
+                    },
+                    "ip": {
+                      "type": "string",
+                      "example": "10.1.1.0/24",
+                      "description": "IP source address"
+                    },
+                    "tcpPort": {
+                      "type": "integer",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "TCP source address"
+                    },
+                    "udpPort": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "UDP source address"
+                    },
+                    "sctpPort": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "SCTP source address"
+                    },
+                    "icmpType": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
+                    },
+                    "icmpCode": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
+                    },
+                    "flowLabel": {
+                      "type": "Hex16",
+                      "format": "Hex16",
+                      "example": "0xffffe",
+                      "description": "IPv6 Flow Label (RFC 6437)"
+                    },
+                    "icmpv6Type": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV6 type (RFC2463)"
+                    },
+                    "icmpv6Code": {
+                      "type": "uint16",
+                      "format": "uint16",
+                      "example": 1,
+                      "description": "Internet Control Message Protocol for IPV6 code (RFC2463)"
+                    },
+                    "targetAddress": {
+                      "type": "String",
+                      "example": "10.1.1.0/24",
+                      "description": "IPv6 Neighbor discovery target address"
+                    },
+                    "label": {
+                      "type": "int32",
+                      "format": "int32",
+                      "example": 1,
+                      "description": "MPLS label"
+                    },
+                    "exthdrFlags": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "IPv6 extension header pseudo-field"
+                    },
+                    "lambda": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "wavelength abstraction"
+                    },
+                    "gridType": {
+                      "type": "String",
+                      "example": "DWDM",
+                      "description": "Type of wavelength grid"
+                    },
+                    "channelSpacing": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 100,
+                      "description": "Optical channel spacing"
+                    },
+                    "spacingMultiplier": {
+                      "type": "integer",
+                      "format": "int64",
+                      "example": 4,
+                      "description": "Optical channel spacing multiplier"
+                    },
+                    "slotGranularity": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 8
+                    },
+                    "ochSignalId": {
+                      "type": "integer",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "Optical channel signal ID"
+                    },
+                    "tunnelId": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 5,
+                      "description": "Tunnel ID"
+                    },
+                    "ochSignalType": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "Optical channel signal type"
+                    },
+                    "oduSignalId": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 1,
+                      "description": "ODU (Optical channel Data Unit) signal ID."
+                    },
+                    "tributaryPortNumber": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 11,
+                      "description": "OPU (Optical channel Payload Unit) port number."
+                    },
+                    "tributarySlotLen": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 80,
+                      "description": "OPU (Optical channel Payload Unit) slot length."
+                    },
+                    "tributarySlotBitmap": {
+                      "type": "array",
+                      "title": "tributarySlotBitmap",
+                      "description": "OPU (Optical channel Payload Unit) slot bitmap.",
+                      "required": [
+                        "byte",
+                        "port"
+                      ],
+                      "items": {
+                        "type": "byte",
+                        "title": "byte",
+                        "example": 1
+                      }
+                    },
+                    "oduSignalType": {
+                      "type": "int64",
+                      "format": "int64",
+                      "example": 4,
+                      "description": "ODU (Optical channel Data Unit) signal type."
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/web/api/src/main/resources/definitions/Flows.json b/web/api/src/main/resources/definitions/Flows.json
deleted file mode 100644
index cb8699a..0000000
--- a/web/api/src/main/resources/definitions/Flows.json
+++ /dev/null
@@ -1,373 +0,0 @@
-{
-  "type": "object",
-  "title": "flows",
-  "required": [
-    "flows"
-  ],
-  "properties": {
-    "flows": {
-      "type": "array",
-      "xml": {
-        "name": "flows",
-        "wrapped": true
-      },
-      "items": {
-        "type": "object",
-        "title": "flow",
-        "required": [
-          "id",
-          "tableId",
-          "appId",
-          "groupId",
-          "priority",
-          "timeout",
-          "isPermanent",
-          "deviceId",
-          "state",
-          "life",
-          "packets",
-          "bytes",
-          "lastSeen"
-        ],
-        "properties": {
-          "id": {
-            "type": "string",
-            "example": "12103425214920339"
-          },
-          "tableId": {
-            "type": "integer",
-            "format": "int32",
-            "example": 3
-          },
-          "appId": {
-            "type": "string",
-            "example": "org.onosproject.core"
-          },
-          "groupId": {
-            "type": "integer",
-            "format": "int64",
-            "example": 0
-          },
-          "priority": {
-            "type": "integer",
-            "format": "int32",
-            "example": 40000
-          },
-          "timeout": {
-            "type": "integer",
-            "format": "int32",
-            "example": 0
-          },
-          "isPermanent": {
-            "type": "boolean",
-            "example": true
-          },
-          "deviceId": {
-            "type": "string",
-            "example": "of:0000000000000003"
-          },
-          "state": {
-            "type": "string",
-            "example": "ADDED"
-          },
-          "life": {
-            "type": "integer",
-            "format": "int64",
-            "example": 69889
-          },
-          "packets": {
-            "type": "integer",
-            "format": "int64",
-            "example": 22546
-          },
-          "bytes": {
-            "type": "integer",
-            "format": "int64",
-            "example": 1826226
-          },
-          "lastSeen": {
-            "type": "integer",
-            "format": "int64",
-            "example": 1447892365670
-          },
-          "treatment": {
-            "type": "object",
-            "title": "treatment",
-            "required": [
-              "instructions",
-              "deferred"
-            ],
-            "properties": {
-              "instructions": {
-                "type": "array",
-                "title": "treatment",
-                "required": [
-                  "properties",
-                  "port"
-                ],
-                "items": {
-                  "type": "object",
-                  "title": "instruction",
-                  "required": [
-                    "type",
-                    "port"
-                  ],
-                  "properties": {
-                    "type": {
-                      "type": "string",
-                      "example": "OUTPUT"
-                    },
-                    "port": {
-                      "type": "string",
-                      "example": "CONTROLLER"
-                    }
-                  }
-                }
-              },
-              "deferred": {
-                "type": "array",
-                "xml": {
-                  "name": "deferred",
-                  "wrapped": true
-                },
-                "items": {
-                  "type": "string"
-                }
-              }
-            }
-          }
-        }
-      }
-    },
-    "selector": {
-      "type": "object",
-      "title": "selector",
-      "required": [
-        "criteria"
-      ],
-      "properties": {
-        "criteria": {
-          "type": "array",
-          "xml": {
-            "name": "criteria",
-            "wrapped": true
-          },
-          "items": {
-            "type": "object",
-            "title": "criteria",
-            "properties": {
-              "type": {
-                "type": "string",
-                "description":"Ethernet field name",
-                "example": "ETH_TYPE"
-              },
-              "ethType": {
-                "type": "int64",
-                "format": "int64",
-                "example": "0x88cc",
-                "description":"Ethernet frame type"
-              },
-              "mac": {
-                "type": "string",
-                "example": "00:00:11:00:00:01"
-              },
-              "port": {
-                "type": "int64",
-                "format": "int64",
-                "example": 1,
-                "description":"Match port"
-              },
-              "metadata": {
-                "type": "Hex16",
-                "format": "Hex16",
-                "example": "0xabcdL",
-                "description":"Metadata passed between tables"
-              },
-              "vlanId": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": "0x1000"
-              },
-              "priority": {
-                "type": "int64",
-                "format": "int64",
-                "example": 1,
-                "description":"VLAN priority."
-              },
-              "ipDscp": {
-                "type": "byte",
-                "format": "byte",
-                "description":"IP DSCP (6 bits in ToS field)"
-              },
-              "ipEcn": {
-                "type": "byte",
-                "format": "byte",
-                "description":"IP ECN (2 bits in ToS field)."
-              },
-              "protocol": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": 1,
-                "description":"IP protocol"
-              },
-              "ip": {
-                "type": "string",
-                "example": "10.1.1.0/24",
-                "description":"IP source address"
-              },
-              "tcpPort": {
-                "type": "integer",
-                "format": "uint16",
-                "example": 1,
-                "description":"TCP source address"
-              },
-              "udpPort": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": 1,
-                "description":"UDP source address"
-              },
-              "sctpPort": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": 1,
-                "description":"SCTP source address"
-              },
-              "icmpType": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": 1,
-                "description":"Internet Control Message Protocol for IPV4 code (RFC0792)"
-              },
-              "icmpCode": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": 1,
-                "description":"Internet Control Message Protocol for IPV4 code (RFC0792)"
-              },
-              "flowLabel": {
-                "type": "Hex16",
-                "format": "Hex16",
-                "example": "0xffffe",
-                "description":"IPv6 Flow Label (RFC 6437)"
-              },
-              "icmpv6Type": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": 1,
-                "description":"Internet Control Message Protocol for IPV6 type (RFC2463)"
-              },
-              "icmpv6Code": {
-                "type": "uint16",
-                "format": "uint16",
-                "example": 1,
-                "description":"Internet Control Message Protocol for IPV6 code (RFC2463)"
-              },
-              "targetAddress": {
-                "type": "String",
-                "example": "10.1.1.0/24",
-                "description":"IPv6 Neighbor discovery target address"
-              },
-              "label": {
-                "type": "int32",
-                "format": "int32",
-                "example": 1,
-                "description":"MPLS label"
-              },
-              "exthdrFlags": {
-                "type": "int64",
-                "format": "int64",
-                "example": 1,
-                "description":"IPv6 extension header pseudo-field"
-              },
-              "lambda": {
-                "type": "int64",
-                "format": "int64",
-                "example": 1,
-                "description":"wavelength abstraction"
-              },
-              "gridType": {
-                "type": "String",
-                "example": "DWDM",
-                "description":"Type of wavelength grid"
-              },
-              "channelSpacing": {
-                "type": "int64",
-                "format": "int64",
-                "example": 100,
-                "description":"Optical channel spacing"
-              },
-              "spacingMultiplier": {
-                "type": "integer",
-                "format": "int64",
-                "example": 4,
-                "description":"Optical channel spacing multiplier"
-              },
-              "slotGranularity": {
-                "type": "int64",
-                "format": "int64",
-                "example": 8
-              },
-              "ochSignalId": {
-                "type": "integer",
-                "format": "int64",
-                "example": 1,
-                "description":"Optical channel signal ID"
-              },
-              "tunnelId": {
-                "type": "int64",
-                "format": "int64",
-                "example": 5,
-                "description":"Tunnel ID"
-              },
-              "ochSignalType": {
-                "type": "int64",
-                "format": "int64",
-                "example": 1,
-                "description":"Optical channel signal type"
-              },
-              "oduSignalId": {
-                "type": "int64",
-                "format": "int64",
-                "example": 1,
-                "description":"ODU (Optical channel Data Unit) signal ID."
-              },
-              "tributaryPortNumber": {
-                "type": "int64",
-                "format": "int64",
-                "example": 11,
-                "description":"OPU (Optical channel Payload Unit) port number."
-              },
-              "tributarySlotLen": {
-                "type": "int64",
-                "format": "int64",
-                "example": 80,
-                "description":"OPU (Optical channel Payload Unit) slot length."
-              },
-              "tributarySlotBitmap": {
-                "type": "array",
-                "title": "tributarySlotBitmap",
-                "description":"OPU (Optical channel Payload Unit) slot bitmap.",
-                "required": [
-                  "byte",
-                  "port"
-                ],
-                "items": {
-                  "type": "byte",
-                  "title": "byte",
-                  "example": 1
-                }
-              },
-              "oduSignalType": {
-                "type": "int64",
-                "format": "int64",
-                "example": 4,
-                "description":"ODU (Optical channel Data Unit) signal type."
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
index 4aede15..4687e44 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
@@ -19,6 +19,7 @@
 import com.eclipsesource.json.JsonArray;
 import com.eclipsesource.json.JsonObject;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import org.hamcrest.Description;
 import org.hamcrest.Matchers;
 import org.hamcrest.TypeSafeMatcher;
@@ -29,6 +30,7 @@
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.MacAddress;
 import org.onlab.rest.BaseResource;
+import org.onosproject.app.ApplicationService;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.codec.impl.FlowRuleCodec;
@@ -65,6 +67,7 @@
 
 import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.anyShort;
+import static org.easymock.EasyMock.anyString;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
@@ -98,6 +101,8 @@
     final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
             "", "", "", "", null);
 
+    final ApplicationService mockApplicationService = createMock(ApplicationService.class);
+
     final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
     final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
 
@@ -107,6 +112,14 @@
     final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
     final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
 
+    final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1);
+    final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2);
+
+    final MockFlowRule flowRule3 = new MockFlowRule(deviceId2, 3);
+    final MockFlowRule flowRule4 = new MockFlowRule(deviceId2, 4);
+
+    final Set<FlowRule> flowRules = Sets.newHashSet();
+
     /**
      * Mock class for a flow entry.
      */
@@ -219,6 +232,83 @@
     }
 
     /**
+     * Mock class for a flow rule.
+     */
+    private static class MockFlowRule implements FlowRule {
+
+        final DeviceId deviceId;
+        final long baseValue;
+        TrafficTreatment treatment;
+        TrafficSelector selector;
+
+        public MockFlowRule(DeviceId deviceId, long id) {
+            this.deviceId = deviceId;
+            this.baseValue = id * 100;
+        }
+
+        @Override
+        public FlowId id() {
+            final long id = baseValue + 55;
+            return FlowId.valueOf(id);
+        }
+
+        @Override
+        public short appId() {
+            return 4;
+        }
+
+        @Override
+        public GroupId groupId() {
+            return new DefaultGroupId(3);
+        }
+
+        @Override
+        public int priority() {
+            return 0;
+        }
+
+        @Override
+        public DeviceId deviceId() {
+            return deviceId;
+        }
+
+        @Override
+        public TrafficSelector selector() {
+            return selector;
+        }
+
+        @Override
+        public TrafficTreatment treatment() {
+            return treatment;
+        }
+
+        @Override
+        public int timeout() {
+            return (int) (baseValue + 77);
+        }
+
+        @Override
+        public boolean isPermanent() {
+            return false;
+        }
+
+        @Override
+        public int tableId() {
+            return 0;
+        }
+
+        @Override
+        public boolean exactMatch(FlowRule rule) {
+            return false;
+        }
+
+        @Override
+        public FlowRuleExtPayLoad payLoad() {
+            return null;
+        }
+    }
+
+    /**
      * Populates some flows used as testing data.
      */
     private void setupMockFlows() {
@@ -249,6 +339,26 @@
     }
 
     /**
+     * Populates some flow rules used as testing data.
+     */
+    private void setupMockFlowRules() {
+        flowRule2.treatment = DefaultTrafficTreatment.builder()
+                .setEthDst(MacAddress.BROADCAST)
+                .build();
+        flowRule2.selector = DefaultTrafficSelector.builder()
+                .matchEthType((short) 3)
+                .matchIPProtocol((byte) 9)
+                .build();
+        flowRule4.treatment = DefaultTrafficTreatment.builder()
+                .build();
+
+        flowRules.add(flowRule1);
+        flowRules.add(flowRule2);
+        flowRules.add(flowRule3);
+        flowRules.add(flowRule4);
+    }
+
+    /**
      * Sets up the global values for all the tests.
      */
     @Before
@@ -264,6 +374,8 @@
         // Mock Core Service
         expect(mockCoreService.getAppId(anyShort()))
                 .andReturn(NetTestTools.APP_ID).anyTimes();
+        expect(mockCoreService.getAppId(anyString()))
+                .andReturn(NetTestTools.APP_ID).anyTimes();
         expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
                 .andReturn(APP_ID).anyTimes();
         replay(mockCoreService);
@@ -276,7 +388,8 @@
                         .add(FlowRuleService.class, mockFlowService)
                         .add(DeviceService.class, mockDeviceService)
                         .add(CodecService.class, codecService)
-                        .add(CoreService.class, mockCoreService);
+                        .add(CoreService.class, mockCoreService)
+                        .add(ApplicationService.class, mockApplicationService);
 
         BaseResource.setServiceDirectory(testDirectory);
     }
@@ -294,12 +407,12 @@
      * Hamcrest matcher to check that a flow representation in JSON matches
      * the actual flow entry.
      */
-    public static class FlowJsonMatcher extends TypeSafeMatcher<JsonObject> {
+    public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
         private final FlowEntry flow;
         private final String expectedAppId;
         private String reason = "";
 
-        public FlowJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
+        public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
             flow = flowValue;
             expectedAppId = expectedAppIdValue;
         }
@@ -398,19 +511,19 @@
      * @param flow flow object we are looking for
      * @return matcher
      */
-    private static FlowJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
-        return new FlowJsonMatcher(flow, expectedAppName);
+    private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
+        return new FlowEntryJsonMatcher(flow, expectedAppName);
     }
 
     /**
      * Hamcrest matcher to check that a flow is represented properly in a JSON
      * array of flows.
      */
-    public static class FlowJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
+    public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
         private final FlowEntry flow;
         private String reason = "";
 
-        public FlowJsonArrayMatcher(FlowEntry flowValue) {
+        public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
             flow = flowValue;
         }
 
@@ -452,8 +565,174 @@
      * @param flow flow object we are looking for
      * @return matcher
      */
-    private static FlowJsonArrayMatcher hasFlow(FlowEntry flow) {
-        return new FlowJsonArrayMatcher(flow);
+    private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
+        return new FlowEntryJsonArrayMatcher(flow);
+    }
+
+    /**
+     * Hamcrest matcher to check that a flow representation in JSON matches
+     * the actual flow rule.
+     */
+    public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
+        private final FlowRule flow;
+        private final String expectedAppId;
+        private String reason = "";
+
+        public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
+            flow = flowValue;
+            expectedAppId = expectedAppIdValue;
+        }
+
+        @Override
+        public boolean matchesSafely(JsonObject jsonFlow) {
+            // check id
+            final String jsonId = jsonFlow.get("id").asString();
+            final String flowId = Long.toString(flow.id().value());
+            if (!jsonId.equals(flowId)) {
+                reason = "id " + flow.id().toString();
+                return false;
+            }
+
+            // check application id
+            final String jsonAppId = jsonFlow.get("appId").asString();
+            if (!jsonAppId.equals(expectedAppId)) {
+                reason = "appId " + Short.toString(flow.appId());
+                return false;
+            }
+
+            // check device id
+            final String jsonDeviceId = jsonFlow.get("deviceId").asString();
+            if (!jsonDeviceId.equals(flow.deviceId().toString())) {
+                reason = "deviceId " + flow.deviceId();
+                return false;
+            }
+
+            // check treatment and instructions array
+            if (flow.treatment() != null) {
+                final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
+                final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
+                if (flow.treatment().immediate().size() != jsonInstructions.size()) {
+                    reason = "instructions array size of " +
+                            Integer.toString(flow.treatment().immediate().size());
+                    return false;
+                }
+                for (final Instruction instruction : flow.treatment().immediate()) {
+                    boolean instructionFound = false;
+                    for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
+                        final String jsonType =
+                                jsonInstructions.get(instructionIndex)
+                                        .asObject().get("type").asString();
+                        final String instructionType = instruction.type().name();
+                        if (jsonType.equals(instructionType)) {
+                            instructionFound = true;
+                        }
+                    }
+                    if (!instructionFound) {
+                        reason = "instruction " + instruction.toString();
+                        return false;
+                    }
+                }
+            }
+
+            // check selector and criteria array
+            if (flow.selector() != null) {
+                final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
+                final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
+                if (flow.selector().criteria().size() != jsonCriteria.size()) {
+                    reason = "criteria array size of " +
+                            Integer.toString(flow.selector().criteria().size());
+                    return false;
+                }
+                for (final Criterion criterion : flow.selector().criteria()) {
+                    boolean criterionFound = false;
+
+                    for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
+                        final String jsonType =
+                                jsonCriteria.get(criterionIndex)
+                                        .asObject().get("type").asString();
+                        final String criterionType = criterion.type().name();
+                        if (jsonType.equals(criterionType)) {
+                            criterionFound = true;
+                        }
+                    }
+                    if (!criterionFound) {
+                        reason = "criterion " + criterion.toString();
+                        return false;
+                    }
+                }
+            }
+
+            return true;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText(reason);
+        }
+    }
+
+    /**
+     * Factory to allocate a flow matcher.
+     *
+     * @param flow flow rule object we are looking for
+     * @return matcher
+     */
+    private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
+        return new FlowRuleJsonMatcher(flow, expectedAppName);
+    }
+
+    /**
+     * Hamcrest matcher to check that a flow is represented properly in a JSON
+     * array of flow rules.
+     */
+    public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
+        private final FlowRule flow;
+        private String reason = "";
+
+        public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
+            flow = flowValue;
+        }
+
+        @Override
+        public boolean matchesSafely(JsonArray json) {
+            boolean flowFound = false;
+
+            for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
+                 jsonFlowIndex++) {
+
+                final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
+
+                final String flowId = Long.toString(flow.id().value());
+                final String jsonFlowId = jsonFlow.get("id").asString();
+                if (jsonFlowId.equals(flowId)) {
+                    flowFound = true;
+
+                    //  We found the correct flow, check attribute values
+                    assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
+                }
+            }
+            if (!flowFound) {
+                reason = "Flow with id " + flow.id().toString() + " not found";
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText(reason);
+        }
+    }
+
+    /**
+     * Factory to allocate a flow array matcher.
+     *
+     * @param flow flow rule object we are looking for
+     * @return matcher
+     */
+    private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
+        return new FlowRuleJsonArrayMatcher(flow);
     }
 
     /**
@@ -572,7 +851,7 @@
      * Tests creating a flow with POST.
      */
     @Test
-    public void testPost() {
+    public void testPostWithoutAppId() {
         mockFlowService.applyFlowRules(anyObject());
         expectLastCall();
         replay(mockFlowService);
@@ -590,6 +869,28 @@
     }
 
     /**
+     * Tests creating a flow with POST while specifying application identifier.
+     */
+    @Test
+    public void testPostWithAppId() {
+        mockFlowService.applyFlowRules(anyObject());
+        expectLastCall();
+        replay(mockFlowService);
+
+        WebTarget wt = target();
+        InputStream jsonStream = FlowsResourceTest.class
+                .getResourceAsStream("post-flow.json");
+
+        Response response = wt.path("flows/of:0000000000000001")
+                .queryParam("appId", "org.onosproject.rest")
+                .request(MediaType.APPLICATION_JSON_TYPE)
+                .post(Entity.json(jsonStream));
+        assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+        String location = response.getLocation().getPath();
+        assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
+    }
+
+    /**
      * Tests deleting a flow.
      */
     @Test
@@ -609,4 +910,55 @@
         assertThat(deleteResponse.getStatus(),
                 is(HttpURLConnection.HTTP_NO_CONTENT));
     }
+
+    /**
+     * Tests the result of a rest api GET for an application.
+     */
+    @Test
+    public void testGetFlowByAppId() {
+        setupMockFlowRules();
+
+        expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
+        replay(mockApplicationService);
+
+        expect(mockFlowService.getFlowRulesById(APP_ID)).andReturn(flowRules).anyTimes();
+        replay(mockFlowService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("flows/application/1").request().get(String.class);
+        final JsonObject result = Json.parse(response).asObject();
+        assertThat(result, notNullValue());
+
+        assertThat(result.names(), hasSize(1));
+        assertThat(result.names().get(0), is("flows"));
+        final JsonArray jsonFlows = result.get("flows").asArray();
+        assertThat(jsonFlows, notNullValue());
+        assertThat(jsonFlows, hasFlowRule(flowRule1));
+        assertThat(jsonFlows, hasFlowRule(flowRule2));
+        assertThat(jsonFlows, hasFlowRule(flowRule3));
+        assertThat(jsonFlows, hasFlowRule(flowRule4));
+    }
+
+    /**
+     * Tests the result of a rest api DELETE for an application.
+     */
+    @Test
+    public void testRemoveFlowByAppId() {
+        expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
+        replay(mockApplicationService);
+
+        mockFlowService.removeFlowRulesById(APP_ID);
+        expectLastCall();
+        replay(mockFlowService);
+
+        WebTarget wt = target();
+
+        String location = "/flows/application/1";
+
+        Response deleteResponse = wt.path(location)
+                .request()
+                .delete();
+        assertThat(deleteResponse.getStatus(),
+                is(HttpURLConnection.HTTP_NO_CONTENT));
+    }
 }