Allow to specify appId through FlowObjective REST API

Change-Id: Iadff74d379e1d5ec4f6e8ff2cda2ad96892d2cc1
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/FilteringObjectiveCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/FilteringObjectiveCodec.java
index 67aae37..673e4c8 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/FilteringObjectiveCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/FilteringObjectiveCodec.java
@@ -20,7 +20,6 @@
 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.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -37,7 +36,7 @@
 /**
  * Filtering Objective Codec.
  */
-public class FilteringObjectiveCodec extends JsonCodec<FilteringObjective> {
+public final class FilteringObjectiveCodec extends JsonCodec<FilteringObjective> {
     private final Logger log = getLogger(getClass());
 
     // JSON field names
@@ -45,6 +44,7 @@
     private static final String TYPE = "type";
     private static final String KEY = "key";
     private static final String META = "meta";
+    private static final String APP_ID = "appId";
     private static final String OPERATION = "operation";
     private static final String CONDITIONS = "conditions";
 
@@ -118,9 +118,12 @@
         final DefaultFilteringObjective.Builder builder =
                 (DefaultFilteringObjective.Builder) och.decode(json, baseBuilder, context);
 
+
+
         // application id
-        ApplicationId appId = coreService.registerApplication(REST_APP_ID);
-        builder.fromApp(appId);
+        JsonNode appIdJson = json.get(APP_ID);
+        String appId = appIdJson != null ? appIdJson.asText() : REST_APP_ID;
+        builder.fromApp(coreService.registerApplication(appId));
 
         // decode type
         String typeStr = nullIsIllegal(json.get(TYPE), TYPE + MISSING_MEMBER_MESSAGE).asText();
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/ForwardingObjectiveCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/ForwardingObjectiveCodec.java
index b198fa3..864756a 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/ForwardingObjectiveCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/ForwardingObjectiveCodec.java
@@ -19,7 +19,6 @@
 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.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -34,11 +33,12 @@
 /**
  * Forwarding Objective Codec.
  */
-public class ForwardingObjectiveCodec extends JsonCodec<ForwardingObjective> {
+public final class ForwardingObjectiveCodec extends JsonCodec<ForwardingObjective> {
     private final Logger log = getLogger(getClass());
 
     // JSON field names
     private static final String ID = "id";
+    private static final String APP_ID = "appId";
     private static final String SELECTOR = "selector";
     private static final String FLAG = "flag";
     private static final String OPERATION = "operation";
@@ -116,8 +116,9 @@
                 (DefaultForwardingObjective.Builder) och.decode(json, baseBuilder, context);
 
         // application id
-        ApplicationId appId = coreService.registerApplication(REST_APP_ID);
-        builder.fromApp(appId);
+        JsonNode appIdJson = json.get(APP_ID);
+        String appId = appIdJson != null ? appIdJson.asText() : REST_APP_ID;
+        builder.fromApp(coreService.registerApplication(appId));
 
         // decode flag
         String flagStr = nullIsIllegal(json.get(FLAG), FLAG + MISSING_MEMBER_MESSAGE).asText();
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/NextObjectiveCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/NextObjectiveCodec.java
index 617a1b3..a58ad3f 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/NextObjectiveCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/NextObjectiveCodec.java
@@ -20,7 +20,6 @@
 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.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -37,12 +36,13 @@
 /**
  * Next Objective Codec.
  */
-public class NextObjectiveCodec extends JsonCodec<NextObjective> {
+public final class NextObjectiveCodec extends JsonCodec<NextObjective> {
 
     private final Logger log = getLogger(getClass());
 
     // JSON field names
     private static final String ID = "id";
+    private static final String APP_ID = "appId";
     private static final String TYPE = "type";
     private static final String OPERATION = "operation";
     private static final String TREATMENTS = "treatments";
@@ -121,8 +121,9 @@
         builder.withId(idJson.asInt());
 
         // decode application id
-        ApplicationId appId = coreService.registerApplication(REST_APP_ID);
-        builder.fromApp(appId);
+        JsonNode appIdJson = json.get(APP_ID);
+        String appId = appIdJson != null ? appIdJson.asText() : REST_APP_ID;
+        builder.fromApp(coreService.registerApplication(appId));
 
         // decode type
         String typeStr = nullIsIllegal(json.get(TYPE), TYPE + MISSING_MEMBER_MESSAGE).asText();
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/FilteringObjectiveCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/FilteringObjectiveCodecTest.java
index 87918e4..7718c81 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/FilteringObjectiveCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/FilteringObjectiveCodecTest.java
@@ -21,7 +21,9 @@
 import org.junit.Test;
 import org.onlab.packet.VlanId;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flowobjective.DefaultFilteringObjective;
@@ -47,6 +49,7 @@
     MockCodecContext context;
     JsonCodec<FilteringObjective> filteringObjectiveCodec;
     final CoreService mockCoreService = createMock(CoreService.class);
+    static final String SAMPLE_APP_ID = "org.onosproject.sample";
 
     /**
      * Sets up for each test.
@@ -58,9 +61,6 @@
         filteringObjectiveCodec = context.codec(FilteringObjective.class);
         assertThat(filteringObjectiveCodec, notNullValue());
 
-        expect(mockCoreService.registerApplication(FilteringObjectiveCodec.REST_APP_ID))
-                .andReturn(APP_ID).anyTimes();
-        replay(mockCoreService);
         context.registerService(CoreService.class, mockCoreService);
     }
 
@@ -93,6 +93,12 @@
      */
     @Test
     public void testFilteringObjectiveDecode() throws IOException {
+
+        ApplicationId appId = new DefaultApplicationId(0, SAMPLE_APP_ID);
+
+        expect(mockCoreService.registerApplication(SAMPLE_APP_ID)).andReturn(appId).anyTimes();
+        replay(mockCoreService);
+
         FilteringObjective filteringObjective = getFilteringObjective("FilteringObjective.json");
 
         assertThat(filteringObjective.type(), is(FilteringObjective.Type.PERMIT));
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/ForwardingObjectiveCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/ForwardingObjectiveCodecTest.java
index 9b4e2b1..b6cc7a6 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/ForwardingObjectiveCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/ForwardingObjectiveCodecTest.java
@@ -21,7 +21,9 @@
 import org.junit.Test;
 import org.onlab.packet.VlanId;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criteria;
@@ -32,9 +34,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.*;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
@@ -49,6 +49,7 @@
     MockCodecContext context;
     JsonCodec<ForwardingObjective> forwardingObjectiveCodec;
     final CoreService mockCoreService = createMock(CoreService.class);
+    static final String SAMPLE_APP_ID = "org.onosproject.sample";
 
     /**
      * Sets up for each test.
@@ -60,9 +61,6 @@
         forwardingObjectiveCodec = context.codec(ForwardingObjective.class);
         assertThat(forwardingObjectiveCodec, notNullValue());
 
-        expect(mockCoreService.registerApplication(ForwardingObjectiveCodec.REST_APP_ID))
-                .andReturn(APP_ID).anyTimes();
-        replay(mockCoreService);
         context.registerService(CoreService.class, mockCoreService);
     }
 
@@ -97,6 +95,12 @@
      */
     @Test
     public void testForwardingObjectiveDecode() throws IOException {
+
+        ApplicationId appId = new DefaultApplicationId(0, SAMPLE_APP_ID);
+
+        expect(mockCoreService.registerApplication(SAMPLE_APP_ID)).andReturn(appId).anyTimes();
+        replay(mockCoreService);
+
         ForwardingObjective forwardingObjective = getForwardingObjective("ForwardingObjective.json");
 
         assertThat(forwardingObjective.flag(), is(ForwardingObjective.Flag.SPECIFIC));
@@ -104,6 +108,7 @@
         assertThat(forwardingObjective.timeout(), is(1));
         assertThat(forwardingObjective.op(), is(ForwardingObjective.Operation.ADD));
         assertThat(forwardingObjective.permanent(), is(false));
+        assertThat(forwardingObjective.appId().name(), is(SAMPLE_APP_ID));
     }
 
     /**
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/NextObjectiveCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/NextObjectiveCodecTest.java
index c0fc138..04746db 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/NextObjectiveCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/NextObjectiveCodecTest.java
@@ -20,7 +20,9 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flowobjective.DefaultNextObjective;
@@ -46,6 +48,7 @@
     MockCodecContext context;
     JsonCodec<NextObjective> nextObjectiveCodec;
     final CoreService mockCoreService = createMock(CoreService.class);
+    static final String SAMPLE_APP_ID = "org.onosproject.sample";
 
     /**
      * Sets up for each test.
@@ -57,9 +60,6 @@
         nextObjectiveCodec = context.codec(NextObjective.class);
         assertThat(nextObjectiveCodec, notNullValue());
 
-        expect(mockCoreService.registerApplication(NextObjectiveCodec.REST_APP_ID))
-                .andReturn(APP_ID).anyTimes();
-        replay(mockCoreService);
         context.registerService(CoreService.class, mockCoreService);
     }
 
@@ -89,6 +89,12 @@
      */
     @Test
     public void testNextObjectiveDecode() throws IOException {
+
+        ApplicationId appId = new DefaultApplicationId(0, SAMPLE_APP_ID);
+
+        expect(mockCoreService.registerApplication(SAMPLE_APP_ID)).andReturn(appId).anyTimes();
+        replay(mockCoreService);
+
         NextObjective nextObjective = getNextObjective("NextObjective.json");
 
         assertThat(nextObjective.type(), is(NextObjective.Type.FAILOVER));
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/FilteringObjective.json b/core/common/src/test/resources/org/onosproject/codec/impl/FilteringObjective.json
index 414e3d6..f6c474c 100644
--- a/core/common/src/test/resources/org/onosproject/codec/impl/FilteringObjective.json
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/FilteringObjective.json
@@ -1,5 +1,6 @@
 {
   "priority": 60,
+  "appId": "org.onosproject.sample",
   "isPermanent": "false",
   "timeout": 1,
   "type": "PERMIT",
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/ForwardingObjective.json b/core/common/src/test/resources/org/onosproject/codec/impl/ForwardingObjective.json
index e979626..63f145d 100644
--- a/core/common/src/test/resources/org/onosproject/codec/impl/ForwardingObjective.json
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/ForwardingObjective.json
@@ -1,5 +1,6 @@
 {
   "priority": 60,
+  "appId": "org.onosproject.sample",
   "isPermanent": "false",
   "timeout": 1,
   "flag": "SPECIFIC",
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/NextObjective.json b/core/common/src/test/resources/org/onosproject/codec/impl/NextObjective.json
index 6f52f11..65ba894 100644
--- a/core/common/src/test/resources/org/onosproject/codec/impl/NextObjective.json
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/NextObjective.json
@@ -1,5 +1,6 @@
 {
   "id": 1,
+  "appId": "org.onosproject.sample",
   "type": "FAILOVER",
   "operation": "ADD",
   "treatments": [
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java
index 615e5b1..712ccae 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java
@@ -30,6 +30,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;
@@ -57,6 +58,7 @@
     /**
      * Creates and installs a new filtering objective for the specified device.
      *
+     * @param appId    application identifier
      * @param deviceId device identifier
      * @param stream   filtering objective JSON
      * @return status of the request - CREATED if the JSON is correct,
@@ -67,12 +69,18 @@
     @Path("{deviceId}/filter")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response createFilteringObjective(@PathParam("deviceId") String deviceId,
+    public Response createFilteringObjective(@QueryParam("appId") String appId,
+                                             @PathParam("deviceId") String deviceId,
                                              InputStream stream) {
         try {
             UriBuilder locationBuilder = null;
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
             if (validateDeviceId(deviceId, jsonTree)) {
+
+                if (appId != null) {
+                    jsonTree.put("appId", appId);
+                }
+
                 DeviceId did = DeviceId.deviceId(deviceId);
                 FilteringObjective filteringObjective =
                         codec(FilteringObjective.class).decode(jsonTree, this);
@@ -94,6 +102,7 @@
     /**
      * Creates and installs a new forwarding objective for the specified device.
      *
+     * @param appId    application identifier
      * @param deviceId device identifier
      * @param stream   forwarding objective JSON
      * @return status of the request - CREATED if the JSON is correct,
@@ -104,12 +113,18 @@
     @Path("{deviceId}/forward")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response createForwardingObjective(@PathParam("deviceId") String deviceId,
+    public Response createForwardingObjective(@QueryParam("appId") String appId,
+                                              @PathParam("deviceId") String deviceId,
                                               InputStream stream) {
         try {
             UriBuilder locationBuilder = null;
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
             if (validateDeviceId(deviceId, jsonTree)) {
+
+                if (appId != null) {
+                    jsonTree.put("appId", appId);
+                }
+
                 DeviceId did = DeviceId.deviceId(deviceId);
                 ForwardingObjective forwardingObjective =
                         codec(ForwardingObjective.class).decode(jsonTree, this);
@@ -131,6 +146,7 @@
     /**
      * Creates and installs a new next objective for the specified device.
      *
+     * @param appId    application identifier
      * @param deviceId device identifier
      * @param stream   next objective JSON
      * @return status of the request - CREATED if the JSON is correct,
@@ -141,12 +157,18 @@
     @Path("{deviceId}/next")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response createNextObjective(@PathParam("deviceId") String deviceId,
+    public Response createNextObjective(@QueryParam("appId") String appId,
+                                        @PathParam("deviceId") String deviceId,
                                         InputStream stream) {
         try {
             UriBuilder locationBuilder = null;
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
             if (validateDeviceId(deviceId, jsonTree)) {
+
+                if (appId != null) {
+                    jsonTree.put("appId", appId);
+                }
+
                 DeviceId did = DeviceId.deviceId(deviceId);
                 NextObjective nextObjective =
                         codec(NextObjective.class).decode(jsonTree, this);