[ONOS-3163] Flow classifier web resource UT. and web resource fixes.

Change-Id: Ie23a450e2944c969753af5f5afb38780833b5a7e
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java
index 7a57c0a..ee0dec3 100644
--- a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/FlowClassifierWebResource.java
@@ -15,15 +15,12 @@
  */
 package org.onosproject.vtnweb.resources;
 
-import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
-import static org.onlab.util.Tools.nullIsNotFound;
 import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.Status.OK;
+import static org.onlab.util.Tools.nullIsNotFound;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.UUID;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -36,13 +33,17 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.onosproject.rest.AbstractWebResource;
 import org.onosproject.vtnrsc.FlowClassifier;
 import org.onosproject.vtnrsc.FlowClassifierId;
-import org.onosproject.rest.AbstractWebResource;
 import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
 import org.onosproject.vtnweb.web.FlowClassifierCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 /**
@@ -51,106 +52,84 @@
 @Path("flow_classifiers")
 public class FlowClassifierWebResource extends AbstractWebResource {
 
+    private final Logger log = LoggerFactory.getLogger(FlowClassifierWebResource.class);
+
     final FlowClassifierService service = get(FlowClassifierService.class);
-    final ObjectNode root = mapper().createObjectNode();
     public static final String FLOW_CLASSIFIER_NOT_FOUND = "Flow classifier not found";
 
     /**
-     * Get all flow classifiers created. Returns list of all flow classifiers
-     * created.
+     * Get all flow classifiers created.
      *
-     * @return 200 OK
+     * @return 200 OK, 404 if given flow classifier does not exist
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public Response getFlowClassifiers() {
-        Iterable<FlowClassifier> flowClassifiers = service.getFlowClassifiers();
+        final Iterable<FlowClassifier> flowClassifiers = service.getFlowClassifiers();
         ObjectNode result = new ObjectMapper().createObjectNode();
-        result.set("flow_classifiers", new FlowClassifierCodec().encode(flowClassifiers, this));
+        ArrayNode flowClassifierEntry = result.putArray("flow_classifiers");
+        if (flowClassifiers != null) {
+            for (final FlowClassifier flowClassifier : flowClassifiers) {
+                flowClassifierEntry.add(new FlowClassifierCodec().encode(flowClassifier, this));
+            }
+        }
         return ok(result.toString()).build();
     }
 
     /**
-     * Get details of a flow classifier. Returns details of a specified flow
-     * classifier id.
+     * Get details of a flow classifier.
      *
      * @param id flow classifier id
-     * @return 200 OK
+     * @return 200 OK , 404 if given identifier does not exist
      */
     @GET
     @Path("{flow_id}")
     @Produces(MediaType.APPLICATION_JSON)
     public Response getFlowClassifier(@PathParam("flow_id") String id) {
 
-        if (!service.hasFlowClassifier(FlowClassifierId.of(UUID.fromString(id)))) {
+        if (!service.hasFlowClassifier(FlowClassifierId.of(id))) {
             return Response.status(NOT_FOUND).entity(FLOW_CLASSIFIER_NOT_FOUND).build();
         }
-        FlowClassifier flowClassifier = nullIsNotFound(
-                service.getFlowClassifier(FlowClassifierId.of(UUID.fromString(id))),
+        FlowClassifier flowClassifier = nullIsNotFound(service.getFlowClassifier(FlowClassifierId.of(id)),
                 FLOW_CLASSIFIER_NOT_FOUND);
 
         ObjectNode result = new ObjectMapper().createObjectNode();
         result.set("flow_classifier", new FlowClassifierCodec().encode(flowClassifier, this));
+
         return ok(result.toString()).build();
     }
 
     /**
      * Creates and stores a new flow classifier.
      *
-     * @param flowClassifierId flow classifier identifier
      * @param stream flow classifier from JSON
      * @return status of the request - CREATED if the JSON is correct,
-     *         BAD_REQUEST if the JSON is invalid
-     */
-    @POST
-    @Path("{flow_id}")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response createFlowClassifier(@PathParam("flow_id") String flowClassifierId, InputStream stream) {
-        URI location;
-        try {
-            ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
-
-            FlowClassifier flowClassifier = codec(FlowClassifier.class).decode(jsonTree, this);
-            service.createFlowClassifier(flowClassifier);
-            location = new URI(flowClassifierId);
-        } catch (IOException | URISyntaxException ex) {
-            throw new IllegalArgumentException(ex);
-        }
-        return Response.created(location).build();
-    }
-
-    /**
-     * Creates and stores a new flow classifier.
-     *
-     * @param stream flow classifier from JSON
-     * @return status of the request - CREATED if the JSON is correct,
-     *         BAD_REQUEST if the JSON is invalid
+     * BAD_REQUEST if the JSON is invalid
      */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createFlowClassifier(InputStream stream) {
-        URI location;
         try {
-            ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
+            ObjectMapper mapper = new ObjectMapper();
+            ObjectNode jsonTree = (ObjectNode) mapper.readTree(stream);
+            JsonNode flow = jsonTree.get("flow_classifier");
 
-            FlowClassifier flowClassifier = codec(FlowClassifier.class).decode(jsonTree, this);
-            service.createFlowClassifier(flowClassifier);
-            location = new URI(flowClassifier.flowClassifierId().toString());
-        } catch (IOException | URISyntaxException ex) {
+            FlowClassifier flowClassifier = new FlowClassifierCodec().decode((ObjectNode) flow, this);
+            Boolean issuccess = nullIsNotFound(service.createFlowClassifier(flowClassifier), FLOW_CLASSIFIER_NOT_FOUND);
+            return Response.status(OK).entity(issuccess.toString()).build();
+        } catch (IOException ex) {
+            log.error("Exception while creating flow classifier {}.", ex.toString());
             throw new IllegalArgumentException(ex);
         }
-        return Response.created(location).build();
     }
 
     /**
-     * Update details of a flow classifier. Update details of a specified flow
-     * classifier id.
+     * Update details of a flow classifier.
      *
      * @param id flow classifier id
      * @param stream InputStream
-     * @return 200 OK
+     * @return 200 OK, 404 if given identifier does not exist
      */
     @PUT
     @Path("{flow_id}")
@@ -158,35 +137,29 @@
     @Consumes(MediaType.APPLICATION_JSON)
     public Response updateFlowClassifier(@PathParam("flow_id") String id, final InputStream stream) {
         try {
-            ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
-            FlowClassifier flowClassifier = codec(FlowClassifier.class).decode(jsonTree, this);
+
+            JsonNode jsonTree = mapper().readTree(stream);
+            JsonNode flow = jsonTree.get("flow_classifier");
+            FlowClassifier flowClassifier = new FlowClassifierCodec().decode((ObjectNode) flow, this);
             Boolean result = nullIsNotFound(service.updateFlowClassifier(flowClassifier), FLOW_CLASSIFIER_NOT_FOUND);
-            if (!result) {
-                return Response.status(204).entity(FLOW_CLASSIFIER_NOT_FOUND).build();
-            }
-            return Response.status(203).entity(result.toString()).build();
-        } catch (Exception e) {
-            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()).build();
+            return Response.status(OK).entity(result.toString()).build();
+        } catch (IOException e) {
+            log.error("Update flow classifier failed because of exception {}.", e.toString());
+            throw new IllegalArgumentException(e);
         }
     }
 
     /**
-     * Delete details of a flow classifier. Delete details of a specified flow
-     * classifier id.
+     * Delete details of a flow classifier.
      *
      * @param id flow classifier id
-     * @return 200 OK
-     * @throws IOException when input doesn't match.
      */
     @Path("{flow_id}")
     @DELETE
-    public Response deleteFlowClassifier(@PathParam("flow_id") String id) throws IOException {
-        try {
-            FlowClassifierId flowClassifierId = FlowClassifierId.of(UUID.fromString(id));
-            service.removeFlowClassifier(flowClassifierId);
-            return Response.status(201).entity("SUCCESS").build();
-        } catch (Exception e) {
-            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()).build();
-        }
+    public void deleteFlowClassifier(@PathParam("flow_id") String id) {
+        log.debug("Deletes flow classifier by identifier {}.", id);
+        FlowClassifierId flowClassifierId = FlowClassifierId.of(id);
+        Boolean issuccess = nullIsNotFound(service.removeFlowClassifier(flowClassifierId), FLOW_CLASSIFIER_NOT_FOUND);
+
     }
 }
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java
new file mode 100644
index 0000000..67f8f35
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.IpPrefix;
+import org.onlab.rest.BaseResource;
+import org.onosproject.vtnrsc.FlowClassifier;
+import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
+
+import com.eclipsesource.json.JsonObject;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+/**
+ * Unit tests for flow classifier REST APIs.
+ */
+public class FlowClassifierResourceTest extends VtnResourceTest {
+
+    final FlowClassifierService flowClassifierService = createMock(FlowClassifierService.class);
+
+    FlowClassifierId flowClassifierId1 = FlowClassifierId.of("4a334cd4-fe9c-4fae-af4b-321c5e2eb051");
+    TenantId tenantId1 = TenantId.tenantId("1814726e2d22407b8ca76db5e567dcf1");
+    VirtualPortId srcPortId1 = VirtualPortId.portId("dace4513-24fc-4fae-af4b-321c5e2eb3d1");
+    VirtualPortId dstPortId1 = VirtualPortId.portId("aef3478a-4a56-2a6e-cd3a-9dee4e2ec345");
+
+    final MockFlowClassifier flowClassifier1 = new MockFlowClassifier(flowClassifierId1, tenantId1, "flowClassifier1",
+                                                                      "Mock flow classifier", "IPv4", "IP", 1001, 1500,
+                                                                      5001, 6000, IpPrefix.valueOf("1.1.1.1/16"),
+                                                                      IpPrefix.valueOf("22.12.34.45/16"),
+                                                                      srcPortId1, dstPortId1);
+
+    /**
+     * Mock class for a flow classifier.
+     */
+    private static class MockFlowClassifier implements FlowClassifier {
+
+        private final FlowClassifierId flowClassifierId;
+        private final TenantId tenantId;
+        private final String name;
+        private final String description;
+        private final String etherType;
+        private final String protocol;
+        private final int minSrcPortRange;
+        private final int maxSrcPortRange;
+        private final int minDstPortRange;
+        private final int maxDstPortRange;
+        private final IpPrefix srcIpPrefix;
+        private final IpPrefix dstIpPrefix;
+        private final VirtualPortId srcPort;
+        private final VirtualPortId dstPort;
+
+        public MockFlowClassifier(FlowClassifierId flowClassifierId, TenantId tenantId, String name,
+                                  String description, String etherType, String protocol, int minSrcPortRange,
+                                  int maxSrcPortRange, int minDstPortRange, int maxDstPortRange, IpPrefix srcIpPrefix,
+                                  IpPrefix dstIpPrefix, VirtualPortId srcPort, VirtualPortId dstPort) {
+            this.flowClassifierId = flowClassifierId;
+            this.tenantId = tenantId;
+            this.name = name;
+            this.description = description;
+            this.etherType = etherType;
+            this.protocol = protocol;
+            this.minSrcPortRange = minSrcPortRange;
+            this.maxSrcPortRange = maxSrcPortRange;
+            this.minDstPortRange = minDstPortRange;
+            this.maxDstPortRange = maxDstPortRange;
+            this.srcIpPrefix = srcIpPrefix;
+            this.dstIpPrefix = dstIpPrefix;
+            this.srcPort = srcPort;
+            this.dstPort = dstPort;
+        }
+
+
+        @Override
+        public FlowClassifierId flowClassifierId() {
+            return flowClassifierId;
+        }
+
+        @Override
+        public TenantId tenantId() {
+            return tenantId;
+        }
+
+        @Override
+        public String name() {
+            return name;
+        }
+
+        @Override
+        public String description() {
+            return description;
+        }
+
+        @Override
+        public String etherType() {
+            return etherType;
+        }
+
+        @Override
+        public String protocol() {
+            return protocol;
+        }
+
+        @Override
+        public int minSrcPortRange() {
+            return minSrcPortRange;
+        }
+
+        @Override
+        public int maxSrcPortRange() {
+            return maxSrcPortRange;
+        }
+
+        @Override
+        public int minDstPortRange() {
+            return minDstPortRange;
+        }
+
+        @Override
+        public int maxDstPortRange() {
+            return maxDstPortRange;
+        }
+
+        @Override
+        public IpPrefix srcIpPrefix() {
+            return srcIpPrefix;
+        }
+
+        @Override
+        public IpPrefix dstIpPrefix() {
+            return dstIpPrefix;
+        }
+
+        @Override
+        public VirtualPortId srcPort() {
+            return srcPort;
+        }
+
+        @Override
+        public VirtualPortId dstPort() {
+            return dstPort;
+        }
+
+        @Override
+        public boolean exactMatch(FlowClassifier flowClassifier) {
+            return this.equals(flowClassifier) &&
+                    Objects.equals(this.flowClassifierId, flowClassifier.flowClassifierId()) &&
+                    Objects.equals(this.tenantId, flowClassifier.tenantId());
+        }
+    }
+
+    /**
+     * Sets up the global values for all the tests.
+     */
+    @Before
+    public void setUpTest() {
+        ServiceDirectory testDirectory = new TestServiceDirectory().add(FlowClassifierService.class,
+                                                                        flowClassifierService);
+        BaseResource.setServiceDirectory(testDirectory);
+
+    }
+
+    /**
+     * Cleans up.
+     */
+    @After
+    public void tearDownTest() {
+    }
+
+    /**
+     * Tests the result of the rest api GET when there are no flow classifiers.
+     */
+    @Test
+    public void testFlowClassifiersEmpty() {
+
+        expect(flowClassifierService.getFlowClassifiers()).andReturn(null).anyTimes();
+        replay(flowClassifierService);
+        final WebResource rs = resource();
+        final String response = rs.path("flow_classifiers").get(String.class);
+        assertThat(response, is("{\"flow_classifiers\":[]}"));
+    }
+
+    /**
+     * Tests the result of a rest api GET for flow classifier id.
+     */
+    @Test
+    public void testGetFlowClassifierId() {
+
+        final Set<FlowClassifier> flowClassifiers = new HashSet<>();
+        flowClassifiers.add(flowClassifier1);
+
+        expect(flowClassifierService.hasFlowClassifier(anyObject())).andReturn(true).anyTimes();
+        expect(flowClassifierService.getFlowClassifier(anyObject())).andReturn(flowClassifier1).anyTimes();
+        replay(flowClassifierService);
+
+        final WebResource rs = resource();
+        final String response = rs.path("flow_classifiers/4a334cd4-fe9c-4fae-af4b-321c5e2eb051").get(String.class);
+        final JsonObject result = JsonObject.readFrom(response);
+        assertThat(result, notNullValue());
+    }
+
+    /**
+     * Tests that a fetch of a non-existent flow classifier object throws an exception.
+     */
+    @Test
+    public void testBadGet() {
+        expect(flowClassifierService.getFlowClassifier(anyObject()))
+        .andReturn(null).anyTimes();
+        replay(flowClassifierService);
+        WebResource rs = resource();
+        try {
+            rs.path("flow_classifiers/78dcd363-fc23-aeb6-f44b-56dc5aafb3ae").get(String.class);
+            fail("Fetch of non-existent flow classifier did not throw an exception");
+        } catch (UniformInterfaceException ex) {
+            assertThat(ex.getMessage(),
+                       containsString("returned a response status of"));
+        }
+    }
+
+    /**
+     * Tests creating a flow classifier with POST.
+     */
+    @Test
+    public void testPost() {
+
+        expect(flowClassifierService.createFlowClassifier(anyObject()))
+        .andReturn(true).anyTimes();
+        replay(flowClassifierService);
+
+        WebResource rs = resource();
+        InputStream jsonStream = FlowClassifierResourceTest.class.getResourceAsStream("post-FlowClassifier.json");
+
+        ClientResponse response = rs.path("flow_classifiers")
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, jsonStream);
+        assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
+    }
+
+    /**
+     * Tests deleting a flow classifier.
+     */
+    @Test
+    public void testDelete() {
+        expect(flowClassifierService.removeFlowClassifier(anyObject()))
+        .andReturn(true).anyTimes();
+        replay(flowClassifierService);
+
+        WebResource rs = resource();
+
+        String location = "flow_classifiers/4a334cd4-fe9c-4fae-af4b-321c5e2eb051";
+
+        ClientResponse deleteResponse = rs.path(location)
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .delete(ClientResponse.class);
+        assertThat(deleteResponse.getStatus(),
+                   is(HttpURLConnection.HTTP_NO_CONTENT));
+    }
+}
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/VtnResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/VtnResourceTest.java
new file mode 100644
index 0000000..4b95844
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/VtnResourceTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+
+/**
+ * Base class for VTN REST API tests.  Performs common configuration operations.
+ */
+public class VtnResourceTest extends JerseyTest {
+
+    /**
+     * Assigns an available port for the test.
+     *
+     * @param defaultPort If a port cannot be determined, this one is used.
+     * @return free port
+     */
+    @Override
+    public int getPort(int defaultPort) {
+        try {
+            ServerSocket socket = new ServerSocket(0);
+            socket.setReuseAddress(true);
+            int port = socket.getLocalPort();
+            socket.close();
+            return port;
+        } catch (IOException ioe) {
+            return defaultPort;
+        }
+    }
+
+    @Override
+    public AppDescriptor configure() {
+        return new WebAppDescriptor.Builder("org.onosproject.vtnweb.resources").build();
+    }
+
+}
diff --git a/apps/vtn/vtnweb/src/test/resources/org/onosproject/vtnweb/resources/post-FlowClassifier.json b/apps/vtn/vtnweb/src/test/resources/org/onosproject/vtnweb/resources/post-FlowClassifier.json
new file mode 100644
index 0000000..6e72e8f
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/resources/org/onosproject/vtnweb/resources/post-FlowClassifier.json
@@ -0,0 +1,14 @@
+{"flow_classifier": {
+    "id": "4a334cd4-fe9c-4fae-af4b-321c5e2eb051",
+    "name": "flow1",
+    "tenant_id": "1814726e2d22407b8ca76db5e567dcf1",
+    "description": "flow classifier",
+    "ethertype": "IPv4",
+    "protocol": "tcp",
+    "source_port_range_min": 22, "source_port_range_max": 4000,
+    "destination_port_range_min": 80, "destination_port_range_max": 80,
+    "source_ip_prefix": "1.1.1.1/16" , "destination_ip_prefix": "22.12.34.45/16",
+    "logical_destination_port": "dace4513-24fc-4fae-af4b-321c5e2eb3d1", 
+    "logical_source_port": "aef3478a-4a56-2a6e-cd3a-9dee4e2ec345"
+   }
+}