Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java
index 9855498..468017f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentInstaller.java
@@ -1,8 +1,8 @@
 package org.onlab.onos.net.intent;
 
-import java.util.concurrent.Future;
+import java.util.List;
 
-import org.onlab.onos.net.flow.CompletedBatchOperation;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
 
 /**
  * Abstraction of entity capable of installing intents to the environment.
@@ -14,7 +14,7 @@
      * @param intent intent to be installed
      * @throws IntentException if issues are encountered while installing the intent
      */
-    Future<CompletedBatchOperation> install(T intent);
+    List<FlowRuleBatchOperation> install(T intent);
 
     /**
      * Uninstalls the specified intent from the environment.
@@ -22,5 +22,5 @@
      * @param intent intent to be uninstalled
      * @throws IntentException if issues are encountered while uninstalling the intent
      */
-    Future<CompletedBatchOperation> uninstall(T intent);
+    List<FlowRuleBatchOperation> uninstall(T intent);
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceService.java b/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceService.java
index 0d59a46..d2736b6 100644
--- a/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/resource/LinkResourceService.java
@@ -56,5 +56,5 @@
      * @param link a target link
      * @return available resources for the target link
      */
-    LinkResource availableResources(Link link);
+    LinkResources availableResources(Link link);
 }
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
index 163a056..a51746d 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
@@ -14,12 +14,11 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.concurrent.Future;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.onlab.onos.net.flow.CompletedBatchOperation;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
 
 /**
  * Suite of tests for the intent service contract.
@@ -298,7 +297,7 @@
         }
 
         @Override
-        public Future<CompletedBatchOperation> install(TestInstallableIntent intent) {
+        public List<FlowRuleBatchOperation> install(TestInstallableIntent intent) {
             if (fail) {
                 throw new IntentException("install failed by design");
             }
@@ -306,7 +305,7 @@
         }
 
         @Override
-        public Future<CompletedBatchOperation> uninstall(TestInstallableIntent intent) {
+        public List<FlowRuleBatchOperation> uninstall(TestInstallableIntent intent) {
             if (fail) {
                 throw new IntentException("remove failed by design");
             }
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
index 5824996..cb807d5 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -34,6 +34,8 @@
 import org.onlab.onos.event.AbstractListenerRegistry;
 import org.onlab.onos.event.EventDeliveryService;
 import org.onlab.onos.net.flow.CompletedBatchOperation;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
+import org.onlab.onos.net.flow.FlowRuleService;
 import org.onlab.onos.net.intent.InstallableIntent;
 import org.onlab.onos.net.intent.Intent;
 import org.onlab.onos.net.intent.IntentCompiler;
@@ -90,6 +92,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+
     @Activate
     public void activate() {
         store.setDelegate(delegate);
@@ -283,7 +288,7 @@
         // Indicate that the intent is entering the installing phase.
         store.setState(intent, INSTALLING);
 
-        List<Future<CompletedBatchOperation>> installFutures = Lists.newArrayList();
+        List<FlowRuleBatchOperation> installWork = Lists.newArrayList();
         try {
             List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
             if (installables != null) {
@@ -291,13 +296,13 @@
                     registerSubclassInstallerIfNeeded(installable);
                     trackerService.addTrackedResources(intent.id(),
                                                        installable.requiredLinks());
-                    Future<CompletedBatchOperation> future = getInstaller(installable).install(installable);
-                    installFutures.add(future);
+                    List<FlowRuleBatchOperation> batch = getInstaller(installable).install(installable);
+                    installWork.addAll(batch);
                 }
             }
             // FIXME we have to wait for the installable intents
             //eventDispatcher.post(store.setState(intent, INSTALLED));
-            monitorExecutor.execute(new IntentInstallMonitor(intent, installFutures, INSTALLED));
+            monitorExecutor.execute(new IntentInstallMonitor(intent, installWork, INSTALLED));
         } catch (Exception e) {
             log.warn("Unable to install intent {} due to: {}", intent.id(), e);
             uninstallIntent(intent, RECOMPILING);
@@ -369,16 +374,16 @@
      * @param intent intent to be uninstalled
      */
     private void uninstallIntent(Intent intent, IntentState nextState) {
-        List<Future<CompletedBatchOperation>> uninstallFutures = Lists.newArrayList();
+        List<FlowRuleBatchOperation> uninstallWork = Lists.newArrayList();
         try {
             List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
             if (installables != null) {
                 for (InstallableIntent installable : installables) {
-                    Future<CompletedBatchOperation> future = getInstaller(installable).uninstall(installable);
-                    uninstallFutures.add(future);
+                    List<FlowRuleBatchOperation> batches = getInstaller(installable).uninstall(installable);
+                    uninstallWork.addAll(batches);
                 }
             }
-            monitorExecutor.execute(new IntentInstallMonitor(intent, uninstallFutures, nextState));
+            monitorExecutor.execute(new IntentInstallMonitor(intent, uninstallWork, nextState));
         } catch (IntentException e) {
             log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
         }
@@ -495,17 +500,27 @@
     private class IntentInstallMonitor implements Runnable {
 
         private final Intent intent;
+        private final List<FlowRuleBatchOperation> work;
         private final List<Future<CompletedBatchOperation>> futures;
         private final IntentState nextState;
 
         public IntentInstallMonitor(Intent intent,
-                List<Future<CompletedBatchOperation>> futures, IntentState nextState) {
+                List<FlowRuleBatchOperation> work,
+                IntentState nextState) {
             this.intent = intent;
-            this.futures = futures;
+            this.work = work;
+            // TODO how many Futures can be outstanding? one?
+            this.futures = Lists.newLinkedList();
             this.nextState = nextState;
+
+            // TODO need to kick off the first batch sometime, why not now?
+            futures.add(applyNextBatch());
         }
 
-        private void updateIntent(Intent intent) {
+        /**
+         * Update the intent store with the next status for this intent.
+         */
+        private void updateIntent() {
             if (nextState == RECOMPILING) {
                 executor.execute(new IntentTask(nextState, intent));
             } else if (nextState == INSTALLED || nextState == WITHDRAWN) {
@@ -515,22 +530,55 @@
             }
         }
 
-        @Override
-        public void run() {
+        /**
+         * Apply a list of FlowRules.
+         *
+         * @param rules rules to apply
+         */
+        private Future<CompletedBatchOperation> applyNextBatch() {
+            if (work.isEmpty()) {
+                return null;
+            }
+            FlowRuleBatchOperation batch = work.remove(0);
+            return flowRuleService.applyBatch(batch);
+        }
+
+        /**
+         * Iterate through the pending futures, and remove them when they have completed.
+         */
+        private void processFutures() {
+            List<Future<CompletedBatchOperation>> newFutures = Lists.newArrayList();
             for (Iterator<Future<CompletedBatchOperation>> i = futures.iterator(); i.hasNext();) {
                 Future<CompletedBatchOperation> future = i.next();
                 try {
                     // TODO: we may want to get the future here and go back to the future.
                     CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
-                    // TODO check if future succeeded and if not report fail items
+                    if (completed.isSuccess()) {
+                        Future<CompletedBatchOperation> newFuture = applyNextBatch();
+                        if (newFuture != null) {
+                            // we'll add this later so that we don't get a ConcurrentModException
+                            newFutures.add(newFuture);
+                        }
+                    } else {
+                        // TODO check if future succeeded and if not report fail items
+                        log.warn("Failed items: {}", completed.failedItems());
+                        // TODO revert....
+                        //uninstallIntent(intent, RECOMPILING);
+                    }
                     i.remove();
-
                 } catch (TimeoutException | InterruptedException | ExecutionException te) {
                     log.debug("Intallations of intent {} is still pending", intent);
                 }
             }
+            futures.addAll(newFutures);
+        }
+
+        @Override
+        public void run() {
+            processFutures();
             if (futures.isEmpty()) {
-                updateIntent(intent);
+                // woohoo! we are done!
+                updateIntent();
             } else {
                 // resubmit ourselves if we are not done yet
                 monitorExecutor.submit(this);
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java
index ec668dc..2deb837 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java
@@ -4,7 +4,6 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.List;
-import java.util.concurrent.Future;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -16,14 +15,12 @@
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Link;
 import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.flow.CompletedBatchOperation;
 import org.onlab.onos.net.flow.DefaultFlowRule;
 import org.onlab.onos.net.flow.DefaultTrafficSelector;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleBatchEntry;
 import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
 import org.onlab.onos.net.flow.FlowRuleBatchOperation;
-import org.onlab.onos.net.flow.FlowRuleService;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 import org.onlab.onos.net.intent.IntentExtensionService;
@@ -47,9 +44,6 @@
     protected IntentExtensionService intentManager;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowRuleService flowRuleService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
     private ApplicationId appId;
@@ -65,18 +59,8 @@
         intentManager.unregisterInstaller(PathIntent.class);
     }
 
-    /**
-     * Apply a list of FlowRules.
-     *
-     * @param rules rules to apply
-     */
-    private Future<CompletedBatchOperation> applyBatch(List<FlowRuleBatchEntry> rules) {
-        FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
-        return flowRuleService.applyBatch(batch);
-    }
-
     @Override
-    public Future<CompletedBatchOperation> install(LinkCollectionIntent intent) {
+    public List<FlowRuleBatchOperation> install(LinkCollectionIntent intent) {
         TrafficSelector.Builder builder =
                 DefaultTrafficSelector.builder(intent.selector());
         List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
@@ -92,11 +76,11 @@
                 intent.egressPoint().deviceId(),
                 intent.egressPoint().port()));
 
-        return applyBatch(rules);
+        return Lists.newArrayList(new FlowRuleBatchOperation(rules));
     }
 
     @Override
-    public Future<CompletedBatchOperation> uninstall(LinkCollectionIntent intent) {
+    public List<FlowRuleBatchOperation> uninstall(LinkCollectionIntent intent) {
         TrafficSelector.Builder builder =
                 DefaultTrafficSelector.builder(intent.selector());
         List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
@@ -113,7 +97,7 @@
                intent.egressPoint().deviceId(),
                intent.egressPoint().port()));
 
-        return applyBatch(rules);
+        return Lists.newArrayList(new FlowRuleBatchOperation(rules));
     }
 
     /**
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
index dd6d7e5..0baea5a 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -5,7 +5,6 @@
 
 import java.util.Iterator;
 import java.util.List;
-import java.util.concurrent.Future;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -16,14 +15,12 @@
 import org.onlab.onos.CoreService;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.Link;
-import org.onlab.onos.net.flow.CompletedBatchOperation;
 import org.onlab.onos.net.flow.DefaultFlowRule;
 import org.onlab.onos.net.flow.DefaultTrafficSelector;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleBatchEntry;
 import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
 import org.onlab.onos.net.flow.FlowRuleBatchOperation;
-import org.onlab.onos.net.flow.FlowRuleService;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 import org.onlab.onos.net.intent.IntentExtensionService;
@@ -45,9 +42,6 @@
     protected IntentExtensionService intentManager;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowRuleService flowRuleService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
     private ApplicationId appId;
@@ -63,31 +57,14 @@
         intentManager.unregisterInstaller(PathIntent.class);
     }
 
-    /**
-     * Apply a list of FlowRules.
-     *
-     * @param rules rules to apply
-     */
-    private Future<CompletedBatchOperation> applyBatch(List<FlowRuleBatchEntry> rules) {
-        FlowRuleBatchOperation batch = new FlowRuleBatchOperation(rules);
-        Future<CompletedBatchOperation> future = flowRuleService.applyBatch(batch);
-        return future;
-//        try {
-//            //FIXME don't do this here
-//            future.get();
-//        } catch (InterruptedException | ExecutionException e) {
-//            // TODO Auto-generated catch block
-//            e.printStackTrace();
-//        }
-    }
-
     @Override
-    public Future<CompletedBatchOperation> install(PathIntent intent) {
+    public List<FlowRuleBatchOperation> install(PathIntent intent) {
         TrafficSelector.Builder builder =
                 DefaultTrafficSelector.builder(intent.selector());
         Iterator<Link> links = intent.path().links().iterator();
         ConnectPoint prev = links.next().dst();
         List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
+        // TODO Generate multiple batches
         while (links.hasNext()) {
             builder.matchInport(prev.port());
             Link link = links.next();
@@ -100,18 +77,17 @@
             rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
             prev = link.dst();
         }
-
-        return applyBatch(rules);
+        return Lists.newArrayList(new FlowRuleBatchOperation(rules));
     }
 
     @Override
-    public Future<CompletedBatchOperation> uninstall(PathIntent intent) {
+    public List<FlowRuleBatchOperation> uninstall(PathIntent intent) {
         TrafficSelector.Builder builder =
                 DefaultTrafficSelector.builder(intent.selector());
         Iterator<Link> links = intent.path().links().iterator();
         ConnectPoint prev = links.next().dst();
         List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
-
+        // TODO Generate multiple batches
         while (links.hasNext()) {
             builder.matchInport(prev.port());
             Link link = links.next();
@@ -123,7 +99,7 @@
             rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
             prev = link.dst();
         }
-        return applyBatch(rules);
+        return Lists.newArrayList(new FlowRuleBatchOperation(rules));
     }
 
     // TODO refactor below this line... ----------------------------