ONOS-1242 - REST API for Withdrawing Intents

- upgraded Jersey to 1.19 for some REST bug fixes for Java 8
- consolidated references to Jersey by using the dependecy
  management plugin in the top level pom file.

Change-Id: Ic027f97f8fce6c673c9f2c02a92abaeedfedeb97
diff --git a/features/features.xml b/features/features.xml
index 6f8da0c..ef79503 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -71,9 +71,10 @@
              description="ONOS 3rd party dependencies">
         <feature>war</feature>
         <feature>onos-thirdparty-base</feature>
-        <bundle>mvn:com.sun.jersey/jersey-core/1.18.1</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-server/1.18.1</bundle>
-        <bundle>mvn:com.sun.jersey/jersey-servlet/1.18.1</bundle>
+        <bundle>mvn:com.sun.jersey/jersey-core/1.19</bundle>
+        <bundle>mvn:com.sun.jersey/jersey-server/1.19</bundle>
+        <bundle>mvn:com.sun.jersey/jersey-servlet/1.19</bundle>
+        <bundle>mvn:javax.ws.rs/jsr311-api/1.1.1</bundle>
     </feature>
 
     <feature name="onos-api" version="@FEATURE-VERSION"
diff --git a/pom.xml b/pom.xml
index 35439d9..d487a53 100644
--- a/pom.xml
+++ b/pom.xml
@@ -194,10 +194,22 @@
             <dependency>
                 <groupId>com.sun.jersey</groupId>
                 <artifactId>jersey-servlet</artifactId>
-                <version>1.18.1</version>
+                <version>1.19</version>
                 <scope>provided</scope>
             </dependency>
             <dependency>
+                <groupId>com.sun.jersey.jersey-test-framework</groupId>
+                <artifactId>jersey-test-framework-core</artifactId>
+                <version>1.19</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.sun.jersey.jersey-test-framework</groupId>
+                <artifactId>jersey-test-framework-grizzly2</artifactId>
+                <version>1.19</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
                 <groupId>com.fasterxml.jackson.core</groupId>
                 <artifactId>jackson-databind</artifactId>
                 <version>2.4.2</version>
diff --git a/utils/rest/pom.xml b/utils/rest/pom.xml
index f2664b8..5c80d17 100644
--- a/utils/rest/pom.xml
+++ b/utils/rest/pom.xml
@@ -33,19 +33,13 @@
 
     <dependencies>
         <dependency>
-            <groupId>com.sun.jersey</groupId>
-            <artifactId>jersey-servlet</artifactId>
-        </dependency>
-        <dependency>
             <groupId>com.sun.jersey.jersey-test-framework</groupId>
             <artifactId>jersey-test-framework-core</artifactId>
-            <version>1.18.1</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.sun.jersey.jersey-test-framework</groupId>
             <artifactId>jersey-test-framework-grizzly2</artifactId>
-            <version>1.18.1</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/web/api/src/main/java/org/onosproject/rest/IntentsWebResource.java b/web/api/src/main/java/org/onosproject/rest/IntentsWebResource.java
index 5d1c2f0..7e62953 100644
--- a/web/api/src/main/java/org/onosproject/rest/IntentsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/IntentsWebResource.java
@@ -15,6 +15,11 @@
  */
 package org.onosproject.rest;
 
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -26,18 +31,29 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.net.intent.HostToHostIntent;
 import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
 import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.PointToPointIntent;
+import org.slf4j.Logger;
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
+import static org.onosproject.net.intent.IntentState.FAILED;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * REST resource for interacting with the inventory of intents.
  */
 
 @Path("intents")
 public class IntentsWebResource extends AbstractWebResource {
+    private static final Logger log = getLogger(IntentsWebResource.class);
+    private static final int WITHDRAW_EVENT_TIMEOUT_SECONDS = 5;
+
     public static final String INTENT_NOT_FOUND = "Intent is not found";
 
     /**
@@ -84,4 +100,79 @@
         }
         return ok(root).build();
     }
+
+    class DeleteListener implements IntentListener {
+        final Key key;
+        final CountDownLatch latch;
+
+        DeleteListener(Key key, CountDownLatch latch) {
+            this.key = key;
+            this.latch = latch;
+        }
+
+        @Override
+        public void event(IntentEvent event) {
+            if (Objects.equals(event.subject().key(), key) &&
+                (event.type() == IntentEvent.Type.WITHDRAWN ||
+                        event.type() == IntentEvent.Type.FAILED)) {
+                latch.countDown();
+            }
+        }
+    }
+
+    /**
+     * Uninstalls a single intent by Id.
+     *
+     * @param appId the Application ID
+     * @param keyString the Intent key value to look up
+     */
+    @DELETE
+    @Path("{appId}/{key}")
+    public void deleteIntentById(@PathParam("appId") Short appId,
+                                  @PathParam("key") String keyString) {
+        final ApplicationId app = get(CoreService.class).getAppId(appId);
+
+        Intent intent = get(IntentService.class).getIntent(Key.of(keyString, app));
+        IntentService service = get(IntentService.class);
+
+        if (intent == null) {
+            intent = service
+                    .getIntent(Key.of(Long.parseLong(keyString), app));
+        }
+        if (intent == null) {
+            // No such intent.  REST standards recommend a positive status code
+            // in this case.
+            return;
+        }
+
+
+        Key key = intent.key();
+
+        // set up latch and listener to track uninstall progress
+        CountDownLatch latch = new CountDownLatch(1);
+
+        IntentListener listener = new DeleteListener(key, latch);
+        service.addListener(listener);
+
+        try {
+            // request the withdraw
+            service.withdraw(intent);
+
+            try {
+                latch.await(WITHDRAW_EVENT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                log.info("REST Delete operation timed out waiting for intent {}", key);
+            }
+            // double check the state
+            IntentState state = service.getIntentState(key);
+            if (state == WITHDRAWN || state == FAILED) {
+                service.purge(intent);
+            }
+
+        } finally {
+            // clean up the listener
+            service.removeListener(listener);
+        }
+    }
+
 }
diff --git a/web/pom.xml b/web/pom.xml
index 7f3bfae..8b7fb63 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -79,13 +79,11 @@
         <dependency>
             <groupId>com.sun.jersey.jersey-test-framework</groupId>
             <artifactId>jersey-test-framework-core</artifactId>
-            <version>1.18.1</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.sun.jersey.jersey-test-framework</groupId>
             <artifactId>jersey-test-framework-grizzly2</artifactId>
-            <version>1.18.1</version>
             <scope>test</scope>
         </dependency>