Refactored intermediate IntentUpdate classes

Change-Id: I3d4a435ef4aa97559d5407d49f45519098c3f193
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentInstaller.java b/core/api/src/main/java/org/onosproject/net/intent/IntentInstaller.java
index 6b9e35d..50c609a 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/IntentInstaller.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentInstaller.java
@@ -15,7 +15,10 @@
  */
 package org.onosproject.net.intent;
 
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleBatchEntry;
 import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleOperations;
 
 import java.util.List;
 
@@ -30,7 +33,32 @@
      * @return flow rule operations to complete install
      * @throws IntentException if issues are encountered while installing the intent
      */
+    @Deprecated
     List<FlowRuleBatchOperation> install(T intent);
+    // FIXME
+    default FlowRuleOperations.Builder install2(T intent) {
+        FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+        for (FlowRuleBatchOperation batch : install(intent)) {
+            for (FlowRuleBatchEntry entry : batch.getOperations()) {
+                FlowRule rule = entry.target();
+                switch (entry.operator()) {
+                    case ADD:
+                        builder.add(rule);
+                        break;
+                    case REMOVE:
+                        builder.remove(rule);
+                        break;
+                    case MODIFY:
+                        builder.modify(rule);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            builder.newStage();
+        }
+        return builder;
+    }
 
     /**
      * Uninstalls the specified intent from the environment.
@@ -39,7 +67,32 @@
      * @return flow rule operations to complete uninstall
      * @throws IntentException if issues are encountered while uninstalling the intent
      */
+    @Deprecated
     List<FlowRuleBatchOperation> uninstall(T intent);
+    // FIXME
+    default FlowRuleOperations.Builder uninstall2(T intent) {
+        FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+        for (FlowRuleBatchOperation batch : uninstall(intent)) {
+            for (FlowRuleBatchEntry entry : batch.getOperations()) {
+                FlowRule rule = entry.target();
+                switch (entry.operator()) {
+                    case ADD:
+                        builder.add(rule);
+                        break;
+                    case REMOVE:
+                        builder.remove(rule);
+                        break;
+                    case MODIFY:
+                        builder.modify(rule);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            builder.newStage();
+        }
+        return builder;
+    }
 
     /**
      * Replaces the specified intent with a new one in the environment.
@@ -49,6 +102,31 @@
      * @return flow rule operations to complete the replace
      * @throws IntentException if issues are encountered while uninstalling the intent
      */
+    @Deprecated
     List<FlowRuleBatchOperation> replace(T oldIntent, T newIntent);
+    // FIXME
+    default FlowRuleOperations.Builder replace2(T oldIntent, T newIntent) {
+        FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+        for (FlowRuleBatchOperation batch : replace(oldIntent, newIntent)) {
+            for (FlowRuleBatchEntry entry : batch.getOperations()) {
+                FlowRule rule = entry.target();
+                switch (entry.operator()) {
+                    case ADD:
+                        builder.add(rule);
+                        break;
+                    case REMOVE:
+                        builder.remove(rule);
+                        break;
+                    case MODIFY:
+                        builder.modify(rule);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            builder.newStage();
+        }
+        return builder;
+    }
 
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/Compiling.java b/core/net/src/main/java/org/onosproject/net/intent/impl/Compiling.java
index 5b7953c..e784621 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/Compiling.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/Compiling.java
@@ -16,10 +16,12 @@
 package org.onosproject.net.intent.impl;
 
 import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
 import java.util.Optional;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -30,25 +32,29 @@
 
     // TODO: define an interface and use it, instead of IntentManager
     private final IntentManager intentManager;
-    private final Intent intent;
+    private final IntentData pending;
+    private final IntentData current;
 
-    Compiling(IntentManager intentManager, Intent intent) {
+    Compiling(IntentManager intentManager, IntentData pending, IntentData current) {
         this.intentManager = checkNotNull(intentManager);
-        this.intent = checkNotNull(intent);
+        this.pending = checkNotNull(pending);
+        this.current = current;
     }
 
     @Override
     public Optional<IntentUpdate> execute() {
         try {
-            return Optional.of(new Installing(intentManager, intent, intentManager.compileIntent(intent, null)));
+            List<Intent> installables = (current != null) ? current.installables() : null;
+            pending.setInstallables(intentManager.compileIntent(pending.intent(), installables));
+            return Optional.of(new Installing(intentManager, pending, current));
         } catch (PathNotFoundException e) {
-            log.debug("Path not found for intent {}", intent);
+            log.debug("Path not found for intent {}", pending.intent());
             // TODO: revisit to implement failure handling
-            return Optional.of(new DoNothing());
+            return Optional.of(new CompilingFailed(pending)); //FIXME failed state transition
         } catch (IntentException e) {
-            log.warn("Unable to compile intent {} due to:", intent.id(), e);
+            log.warn("Unable to compile intent {} due to:", pending.intent().id(), e);
             // TODO: revisit to implement failure handling
-            return Optional.of(new DoNothing());
+            return Optional.of(new CompilingFailed(pending)); //FIXME failed state transition
         }
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/CompilingFailed.java b/core/net/src/main/java/org/onosproject/net/intent/impl/CompilingFailed.java
new file mode 100644
index 0000000..f7c25ca
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/CompilingFailed.java
@@ -0,0 +1,40 @@
+/*
+ * 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.net.intent.impl;
+
+import org.onosproject.net.intent.IntentData;
+
+import static org.onosproject.net.intent.IntentState.FAILED;
+
+/**
+ * Represents a phase where the compile has failed.
+ */
+class CompilingFailed implements CompletedIntentUpdate {
+
+    private final IntentData intentData;
+
+    CompilingFailed(IntentData intentData) {
+        this.intentData = intentData;
+        this.intentData.setState(FAILED);
+    }
+
+    @Override
+    public IntentData data() {
+        return intentData;
+    }
+
+    //FIXME we also need to decide what to do with the current intent's resources i.e. cleanup or revert
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/CompletedIntentUpdate.java b/core/net/src/main/java/org/onosproject/net/intent/impl/CompletedIntentUpdate.java
index caddab1..60dc334 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/CompletedIntentUpdate.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/CompletedIntentUpdate.java
@@ -15,11 +15,8 @@
  */
 package org.onosproject.net.intent.impl;
 
-import org.onosproject.net.flow.FlowRuleBatchOperation;
-import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
 
-import java.util.Collections;
-import java.util.List;
 import java.util.Optional;
 
 /**
@@ -27,38 +24,10 @@
  */
 interface CompletedIntentUpdate extends IntentUpdate {
 
-    /**
-     * Moves forward with the contained current batch.
-     * This method is invoked when the batch is successfully completed.
-     */
-    default void batchSuccess() {}
-
-    /**
-     * Reverts the contained batches.
-     * This method is invoked when the batch results in failure.
-     */
-    default void batchFailed() {}
-
-    /**
-     * Returns the current FlowRuleBatchOperation.
-     *
-     * @return current FlowRuleBatchOperation
-     */
-    default FlowRuleBatchOperation currentBatch() {
-        return null;
-    }
-
-    /**
-     * Returns all of installable intents this instance holds.
-     *
-     * @return all of installable intents
-     */
-    default List<Intent> allInstallables() {
-        return Collections.emptyList();
-    }
-
     @Override
     default Optional<IntentUpdate> execute() {
         return Optional.empty();
     }
+
+    IntentData data();
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/DoNothing.java b/core/net/src/main/java/org/onosproject/net/intent/impl/DoNothing.java
deleted file mode 100644
index caf64c1..0000000
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/DoNothing.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.net.intent.impl;
-
-/**
- * Represents a phase doing nothing.
- */
-class DoNothing implements CompletedIntentUpdate {
-}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/InstallRequest.java b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallRequest.java
index 42b9e1d..c22cd35 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/InstallRequest.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallRequest.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.net.intent.impl;
 
-import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentData;
 
 import java.util.Optional;
@@ -27,17 +26,17 @@
 
     // TODO: define an interface and use it, instead of IntentManager
     private final IntentManager intentManager;
-    private final Intent intent;
-    private final IntentData currentState;
+    private final IntentData pending;
 
-    InstallRequest(IntentManager intentManager, Intent intent, IntentData currentState) {
+    InstallRequest(IntentManager intentManager, IntentData intentData) {
         this.intentManager = checkNotNull(intentManager);
-        this.intent = checkNotNull(intent);
-        this.currentState = currentState;
+        this.pending = checkNotNull(intentData);
     }
 
     @Override
     public Optional<IntentUpdate> execute() {
-        return Optional.of(new Compiling(intentManager, intent)); //FIXME
+        //FIXME... store hack
+        IntentData current = intentManager.store.getIntentData(pending.key());
+        return Optional.of(new Compiling(intentManager, pending, current));
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/Installed.java b/core/net/src/main/java/org/onosproject/net/intent/impl/Installed.java
index 316821b..a3a7e1d 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/Installed.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/Installed.java
@@ -15,60 +15,21 @@
  */
 package org.onosproject.net.intent.impl;
 
-import com.google.common.collect.ImmutableList;
-import org.onosproject.net.flow.FlowRuleBatchOperation;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentState;
-
-import java.util.LinkedList;
-import java.util.List;
+import org.onosproject.net.intent.IntentData;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.net.intent.IntentState.FAILED;
 import static org.onosproject.net.intent.IntentState.INSTALLING;
 
 class Installed implements CompletedIntentUpdate {
 
-    // TODO: define an interface and use it, instead of IntentManager
-    private final IntentManager intentManager;
-    private final Intent intent;
-    private final List<Intent> installables;
-    private IntentState intentState;
-    private final List<FlowRuleBatchOperation> batches;
-    private int currentBatch = 0;
+    private final IntentData intentData;
 
-    Installed(IntentManager intentManager,
-              Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
-        this.intentManager = checkNotNull(intentManager);
-        this.intent = checkNotNull(intent);
-        this.installables = ImmutableList.copyOf(checkNotNull(installables));
-        this.batches = new LinkedList<>(checkNotNull(batches));
-        this.intentState = INSTALLING;
+    Installed(IntentData intentData) {
+        this.intentData = checkNotNull(intentData);
+        this.intentData.setState(INSTALLING);
     }
 
-    @Override
-    public void batchSuccess() {
-        currentBatch++;
-    }
-
-    @Override
-    public List<Intent> allInstallables() {
-        return installables;
-    }
-
-    @Override
-    public FlowRuleBatchOperation currentBatch() {
-        return currentBatch < batches.size() ? batches.get(currentBatch) : null;
-    }
-
-    @Override
-    public void batchFailed() {
-        for (int i = batches.size() - 1; i >= currentBatch; i--) {
-            batches.remove(i);
-        }
-        intentState = FAILED;
-        batches.addAll(intentManager.uninstallIntent(intent, installables));
-
-        // TODO we might want to try to recompile the new intent
+    public IntentData data() {
+        return intentData;
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/Installing.java b/core/net/src/main/java/org/onosproject/net/intent/impl/Installing.java
index fae62e1..0d9ce36 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/Installing.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/Installing.java
@@ -15,13 +15,11 @@
  */
 package org.onosproject.net.intent.impl;
 
-import com.google.common.collect.ImmutableList;
-import org.onosproject.net.flow.FlowRuleBatchOperation;
-import org.onosproject.net.intent.Intent;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.intent.IntentData;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.List;
 import java.util.Optional;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -32,26 +30,27 @@
     private static final Logger log = LoggerFactory.getLogger(Installing.class);
 
     private final IntentManager intentManager;
-    private final Intent intent;
-    private final List<Intent> installables;
+    private final IntentData pending;
+    private final IntentData current;
 
     // TODO: define an interface and use it, instead of IntentManager
-    Installing(IntentManager intentManager, Intent intent, List<Intent> installables) {
+    Installing(IntentManager intentManager, IntentData pending, IntentData current) {
         this.intentManager = checkNotNull(intentManager);
-        this.intent = checkNotNull(intent);
-        this.installables = ImmutableList.copyOf(checkNotNull(installables));
+        this.pending = checkNotNull(pending);
+        this.current = current;
     }
 
     @Override
     public Optional<IntentUpdate> execute() {
         try {
-            List<FlowRuleBatchOperation> converted = intentManager.convert(installables);
+            FlowRuleOperations flowRules = intentManager.coordinate(pending.installables());
             // TODO: call FlowRuleService API to push FlowRules and track resources,
             // which the submitted intent will use.
-            return Optional.of(new Installed(intentManager, intent, installables, converted));
+            intentManager.flowRuleService.apply(flowRules);
+            return Optional.of(new Installed(pending));
         } catch (FlowRuleBatchOperationConversionException e) {
-            log.warn("Unable to install intent {} due to:", intent.id(), e.getCause());
-            return Optional.of(new InstallingFailed(intentManager, intent, installables, e.converted()));
+            log.warn("Unable to install intent {} due to:", pending.intent().id(), e.getCause());
+            return Optional.of(new InstallingFailed(pending));
         }
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/InstallingFailed.java b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallingFailed.java
index 0a7a5c4..48e836e 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/InstallingFailed.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallingFailed.java
@@ -15,53 +15,23 @@
  */
 package org.onosproject.net.intent.impl;
 
-import com.google.common.collect.ImmutableList;
-import org.onosproject.net.flow.FlowRuleBatchOperation;
-import org.onosproject.net.intent.Intent;
-
-import java.util.LinkedList;
-import java.util.List;
+import org.onosproject.net.intent.IntentData;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.FAILED;
 
 class InstallingFailed implements CompletedIntentUpdate {
 
-    private IntentManager intentManager;
-    private final Intent intent;
-    private final List<Intent> installables;
-    private final List<FlowRuleBatchOperation> batches;
-    private int currentBatch = 0;
+    private final IntentData intentData;
 
-    InstallingFailed(IntentManager intentManager,
-                     Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
-        this.intentManager = intentManager;
-        this.intent = checkNotNull(intent);
-        this.installables = ImmutableList.copyOf(checkNotNull(installables));
-        this.batches = new LinkedList<>(checkNotNull(batches));
+    InstallingFailed(IntentData intentData) {
+        this.intentData = checkNotNull(intentData);
+        this.intentData.setState(FAILED); //FIXME maybe should be "BROKEN"
+        //TODO consider adding the flow rule operations here
     }
 
     @Override
-    public List<Intent> allInstallables() {
-        return installables;
-    }
-
-    @Override
-    public void batchSuccess() {
-        currentBatch++;
-    }
-
-    @Override
-    public FlowRuleBatchOperation currentBatch() {
-        return currentBatch < batches.size() ? batches.get(currentBatch) : null;
-    }
-
-    @Override
-    public void batchFailed() {
-        for (int i = batches.size() - 1; i >= currentBatch; i--) {
-            batches.remove(i);
-        }
-        batches.addAll(intentManager.uninstallIntent(intent, installables));
-
-        // TODO we might want to try to recompile the new intent
+    public IntentData data() {
+        return intentData;
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
index 373b629..7325dd5 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
@@ -15,23 +15,9 @@
  */
 package org.onosproject.net.intent.impl;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
-
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -42,8 +28,8 @@
 import org.onosproject.core.IdGenerator;
 import org.onosproject.event.AbstractListenerRegistry;
 import org.onosproject.event.EventDeliveryService;
-import org.onosproject.net.flow.CompletedBatchOperation;
 import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleOperations;
 import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentBatchDelegate;
@@ -61,17 +47,25 @@
 import org.onosproject.net.intent.IntentStoreDelegate;
 import org.slf4j.Logger;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newFixedThreadPool;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.namedThreads;
-import static org.onosproject.net.intent.IntentState.FAILED;
-import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
-import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
+import static org.onosproject.net.intent.IntentState.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -285,6 +279,23 @@
         return installable;
     }
 
+    //TODO javadoc
+    //FIXME
+    FlowRuleOperations coordinate(List<Intent> installables) {
+        //List<FlowRuleBatchOperation> batches = new ArrayList<>(installables.size());
+        for (Intent installable : installables) {
+            try {
+                registerSubclassInstallerIfNeeded(installable);
+                //FIXME need to migrate installers to FlowRuleOperations
+                // FIXME need to aggregate the FlowRuleOperations across installables
+                getInstaller(installable).install2(installable).build(null/*FIXME*/);
+            } catch (Exception e) { // TODO this should be IntentException
+                throw new FlowRuleBatchOperationConversionException(null/*FIXME*/, e);
+            }
+        }
+        return null;
+    }
+
     /**
      * Uninstalls all installable intents associated with the given intent.
      *
@@ -292,19 +303,21 @@
      * @param installables installable intents
      * @return list of batches to uninstall intent
      */
-    List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
+    //FIXME
+    FlowRuleOperations uninstallIntent(Intent intent, List<Intent> installables) {
         List<FlowRuleBatchOperation> batches = Lists.newArrayList();
         for (Intent installable : installables) {
             trackerService.removeTrackedResources(intent.id(),
                     installable.resources());
             try {
-                batches.addAll(getInstaller(installable).uninstall(installable));
+                // FIXME need to aggregate the FlowRuleOperations across installables
+                getInstaller(installable).uninstall2(installable).build(null/*FIXME*/);
             } catch (IntentException e) {
                 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
                 // TODO: this should never happen. but what if it does?
             }
         }
-        return batches;
+        return null; //FIXME
     }
 
     /**
@@ -414,12 +427,11 @@
 
     // TODO: simplify the branching statements
     private IntentUpdate createIntentUpdate(IntentData intentData) {
-        IntentData currentState = store.getIntentData(intentData.key());
         switch (intentData.state()) {
             case INSTALL_REQ:
-                return new InstallRequest(this, intentData.intent(), currentState);
+                return new InstallRequest(this, intentData);
             case WITHDRAW_REQ:
-                return new WithdrawRequest(this, intentData.intent(), currentState);
+                return new WithdrawRequest(this, intentData);
             // fallthrough
             case COMPILING:
             case INSTALLING:
@@ -430,21 +442,12 @@
             case FAILED:
             default:
                 // illegal state
-                return new DoNothing();
+                return new CompilingFailed(intentData);
         }
     }
 
-    List<FlowRuleBatchOperation> convert(List<Intent> installables) {
-        List<FlowRuleBatchOperation> batches = new ArrayList<>(installables.size());
-        for (Intent installable : installables) {
-            try {
-                registerSubclassInstallerIfNeeded(installable);
-                batches.addAll(getInstaller(installable).install(installable));
-            } catch (Exception e) { // TODO this should be IntentException
-                throw new FlowRuleBatchOperationConversionException(batches, e);
-            }
-        }
-        return batches;
+    private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
+        return workerExecutor.submit(new IntentWorker(data));
     }
 
     private class IntentBatchPreprocess implements Runnable {
@@ -476,15 +479,13 @@
         @Override
         public void run() {
             try {
-                // 1. wrap each intentdata in a runnable and submit
-                List<Future<IntentUpdate>> updates = createIntentUpdates();
-                // TODO
-                // 2. wait for completion of all the work
-                // 3. accumulate results and submit batch write of IntentData to store
-                //    (we can also try to update these individually)
-
-
-                //new IntentBatchApplyFirst(data, processIntentUpdates(updates), endTime, 0, null).run();
+                /*
+                 1. wrap each intentdata in a runnable and submit
+                 2. wait for completion of all the work
+                 3. accumulate results and submit batch write of IntentData to store
+                    (we can also try to update these individually)
+                 */
+                submitUpdates(waitForFutures(createIntentUpdates()));
             } catch (Exception e) {
                 log.error("Error submitting batches:", e);
                 // FIXME incomplete Intents should be cleaned up
@@ -498,18 +499,33 @@
             }
         }
 
-        private List<Future<IntentUpdate>> createIntentUpdates() {
+        private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
             return data.stream()
-                      .map(IntentManager.this::submitIntentData)
-                      .collect(Collectors.toList());
+                    .map(IntentManager.this::submitIntentData)
+                    .collect(Collectors.toList());
+        }
+
+        private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
+            ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
+            for (Future<CompletedIntentUpdate> future : futures) {
+                try {
+                    updateBuilder.add(future.get());
+                } catch (InterruptedException | ExecutionException e) {
+                    //FIXME
+                    log.warn("Future failed: {}", e);
+                }
+            }
+            return updateBuilder.build();
+        }
+
+        private void submitUpdates(List<CompletedIntentUpdate> updates) {
+            store.batchWrite(updates.stream()
+                                    .map(CompletedIntentUpdate::data)
+                                    .collect(Collectors.toList()));
         }
     }
 
-    private Future<IntentUpdate> submitIntentData(IntentData data) {
-         return workerExecutor.submit(new IntentWorker(data));
-    }
-
-    private class IntentWorker implements Callable<IntentUpdate> {
+    private final class IntentWorker implements Callable<CompletedIntentUpdate> {
 
         private final IntentData data;
 
@@ -518,7 +534,7 @@
         }
 
         @Override
-        public IntentUpdate call() throws Exception {
+        public CompletedIntentUpdate call() throws Exception {
             IntentUpdate update = createIntentUpdate(data);
             Optional<IntentUpdate> currentPhase = Optional.of(update);
             IntentUpdate previousPhase = update;
@@ -527,167 +543,7 @@
                 previousPhase = currentPhase.get();
                 currentPhase = previousPhase.execute();
             }
-            return previousPhase;
-        }
-    }
-
-
-    // TODO: better naming
-    private class IntentBatchApplyFirst extends IntentBatchPreprocess {
-
-        protected final List<CompletedIntentUpdate> intentUpdates;
-        protected final int installAttempt;
-        protected Future<CompletedBatchOperation> future;
-
-        IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
-                              long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
-            super(operations, endTime);
-            this.intentUpdates = ImmutableList.copyOf(intentUpdates);
-            this.future = future;
-            this.installAttempt = installAttempt;
-        }
-
-        @Override
-        public void run() {
-            Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
-            new IntentBatchProcessFutures(data, intentUpdates, endTime, installAttempt, future).run();
-        }
-
-        /**
-         * Builds and applies the next batch, and returns the future.
-         *
-         * @return Future for next batch
-         */
-        protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
-            //TODO test this. (also, maybe save this batch)
-
-            FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
-            if (batch.size() > 0) {
-                //FIXME apply batch might throw an exception
-                return flowRuleService.applyBatch(batch);
-            } else {
-                return null;
-            }
-        }
-
-        private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
-            FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
-            for (CompletedIntentUpdate update : intentUpdates) {
-                FlowRuleBatchOperation currentBatch = update.currentBatch();
-                if (currentBatch != null) {
-                    batch.addAll(currentBatch);
-                }
-            }
-            return batch;
-        }
-
-        protected void abandonShip() {
-            // the batch has failed
-            // TODO: maybe we should do more?
-            log.error("Walk the plank, matey...");
-            future = null;
-            //FIXME
-//            batchService.removeIntentOperations(data);
-        }
-    }
-
-    // TODO: better naming
-    private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
-
-        IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
-                                  long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
-            super(operations, intentUpdates, endTime, installAttempt, future);
-        }
-
-        @Override
-        public void run() {
-            try {
-                Future<CompletedBatchOperation> future = processFutures();
-                if (future == null) {
-                    // there are no outstanding batches; we are done
-                    //FIXME
-                    return; //?
-//                    batchService.removeIntentOperations(data);
-                } else if (System.currentTimeMillis() > endTime) {
-                    // - cancel current FlowRuleBatch and resubmit again
-                    retry();
-                } else {
-                    // we are not done yet, yield the thread by resubmitting ourselves
-                    batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, endTime,
-                            installAttempt, future));
-                }
-            } catch (Exception e) {
-                log.error("Error submitting batches:", e);
-                // FIXME incomplete Intents should be cleaned up
-                //       (transition to FAILED, etc.)
-                abandonShip();
-            }
-        }
-
-        /**
-         * Iterate through the pending futures, and remove them when they have completed.
-         */
-        private Future<CompletedBatchOperation> processFutures() {
-            try {
-                CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
-                updateBatches(completed);
-                return applyNextBatch(intentUpdates);
-            } catch (TimeoutException | InterruptedException te) {
-                log.trace("Installation of intents are still pending: {}", data);
-                return future;
-            } catch (ExecutionException e) {
-                log.warn("Execution of batch failed: {}", data, e);
-                abandonShip();
-                return future;
-            }
-        }
-
-        private void updateBatches(CompletedBatchOperation completed) {
-            if (completed.isSuccess()) {
-                for (CompletedIntentUpdate update : intentUpdates) {
-                    update.batchSuccess();
-                }
-            } else {
-                // entire batch has been reverted...
-                log.debug("Failed items: {}", completed.failedItems());
-                log.debug("Failed ids: {}",  completed.failedIds());
-
-                for (Long id : completed.failedIds()) {
-                    IntentId targetId = IntentId.valueOf(id);
-                    for (CompletedIntentUpdate update : intentUpdates) {
-                        for (Intent intent : update.allInstallables()) {
-                            if (intent.id().equals(targetId)) {
-                                update.batchFailed();
-                                break;
-                            }
-                        }
-                    }
-                    // don't increment the non-failed items, as they have been reverted.
-                }
-            }
-        }
-
-        private void retry() {
-            log.debug("Execution timed out, retrying.");
-            if (future.cancel(true)) { // cancel success; batch is reverted
-                // reset the timer
-                long timeLimit = calculateTimeoutLimit();
-                int attempts = installAttempt + 1;
-                if (attempts == MAX_ATTEMPTS) {
-                    log.warn("Install request timed out: {}", data);
-                    for (CompletedIntentUpdate update : intentUpdates) {
-                        update.batchFailed();
-                    }
-                } else if (attempts > MAX_ATTEMPTS) {
-                    abandonShip();
-                    return;
-                }
-                Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
-                batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, timeLimit, attempts, future));
-            } else {
-                log.error("Cancelling FlowRuleBatch failed.");
-                abandonShip();
-            }
+            return (CompletedIntentUpdate) previousPhase;
         }
     }
 
@@ -700,4 +556,166 @@
             // TODO ensure that only one batch is in flight at a time
         }
     }
+
+//    /////////**************************///////////////////
+//    FIXME Need to build and monitor contexts from FlowRuleService
+//
+//    // TODO: better naming
+//    private class IntentBatchApplyFirst extends IntentBatchPreprocess {
+//
+//        protected final List<CompletedIntentUpdate> intentUpdates;
+//        protected final int installAttempt;
+//        protected Future<CompletedBatchOperation> future;
+//
+//        IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
+//                              long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
+//            super(operations, endTime);
+//            this.intentUpdates = ImmutableList.copyOf(intentUpdates);
+//            this.future = future;
+//            this.installAttempt = installAttempt;
+//        }
+//
+//        @Override
+//        public void run() {
+//            Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
+//            new IntentBatchProcessFutures(data, intentUpdates, endTime, installAttempt, future).run();
+//        }
+//
+//        /**
+//         * Builds and applies the next batch, and returns the future.
+//         *
+//         * @return Future for next batch
+//         */
+//        protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
+//            //TODO test this. (also, maybe save this batch)
+//
+//            FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
+//            if (batch.size() > 0) {
+//                //FIXME apply batch might throw an exception
+//                return flowRuleService.applyBatch(batch);
+//            } else {
+//                return null;
+//            }
+//        }
+//
+//        private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
+//            FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
+//            for (CompletedIntentUpdate update : intentUpdates) {
+//                FlowRuleBatchOperation currentBatch = update.currentBatch();
+//                if (currentBatch != null) {
+//                    batch.addAll(currentBatch);
+//                }
+//            }
+//            return batch;
+//        }
+//
+//        protected void abandonShip() {
+//            // the batch has failed
+//            // TODO: maybe we should do more?
+//            log.error("Walk the plank, matey...");
+//            future = null;
+//            //FIXME
+//            //batchService.removeIntentOperations(data);
+//        }
+//    }
+//
+//    // TODO: better naming
+//    private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
+//
+//        IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
+//                                  long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
+//            super(operations, intentUpdates, endTime, installAttempt, future);
+//        }
+//
+//        @Override
+//        public void run() {
+//            try {
+//                Future<CompletedBatchOperation> future = processFutures();
+//                if (future == null) {
+//                    // there are no outstanding batches; we are done
+//                    //FIXME
+//                    return; //?
+//                    //batchService.removeIntentOperations(data);
+//                } else if (System.currentTimeMillis() > endTime) {
+//                    // - cancel current FlowRuleBatch and resubmit again
+//                    retry();
+//                } else {
+//                    // we are not done yet, yield the thread by resubmitting ourselves
+//                    batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, endTime,
+//                            installAttempt, future));
+//                }
+//            } catch (Exception e) {
+//                log.error("Error submitting batches:", e);
+//                // FIXME incomplete Intents should be cleaned up
+//                //       (transition to FAILED, etc.)
+//                abandonShip();
+//            }
+//        }
+//
+//        /**
+//         * Iterate through the pending futures, and remove them when they have completed.
+//         */
+//        private Future<CompletedBatchOperation> processFutures() {
+//            try {
+//                CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
+//                updateBatches(completed);
+//                return applyNextBatch(intentUpdates);
+//            } catch (TimeoutException | InterruptedException te) {
+//                log.trace("Installation of intents are still pending: {}", data);
+//                return future;
+//            } catch (ExecutionException e) {
+//                log.warn("Execution of batch failed: {}", data, e);
+//                abandonShip();
+//                return future;
+//            }
+//        }
+//
+//        private void updateBatches(CompletedBatchOperation completed) {
+//            if (completed.isSuccess()) {
+//                for (CompletedIntentUpdate update : intentUpdates) {
+//                    update.batchSuccess();
+//                }
+//            } else {
+//                // entire batch has been reverted...
+//                log.debug("Failed items: {}", completed.failedItems());
+//                log.debug("Failed ids: {}",  completed.failedIds());
+//
+//                for (Long id : completed.failedIds()) {
+//                    IntentId targetId = IntentId.valueOf(id);
+//                    for (CompletedIntentUpdate update : intentUpdates) {
+//                        for (Intent intent : update.allInstallables()) {
+//                            if (intent.id().equals(targetId)) {
+//                                update.batchFailed();
+//                                break;
+//                            }
+//                        }
+//                    }
+//                    // don't increment the non-failed items, as they have been reverted.
+//                }
+//            }
+//        }
+//
+//        private void retry() {
+//            log.debug("Execution timed out, retrying.");
+//            if (future.cancel(true)) { // cancel success; batch is reverted
+//                // reset the timer
+//                long timeLimit = calculateTimeoutLimit();
+//                int attempts = installAttempt + 1;
+//                if (attempts == MAX_ATTEMPTS) {
+//                    log.warn("Install request timed out: {}", data);
+//                    for (CompletedIntentUpdate update : intentUpdates) {
+//                        update.batchFailed();
+//                    }
+//                } else if (attempts > MAX_ATTEMPTS) {
+//                    abandonShip();
+//                    return;
+//                }
+//                Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
+//                batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, timeLimit, attempts, future));
+//            } else {
+//                log.error("Cancelling FlowRuleBatch failed.");
+//                abandonShip();
+//            }
+//        }
+//    }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/WithdrawRequest.java b/core/net/src/main/java/org/onosproject/net/intent/impl/WithdrawRequest.java
index dee793b..1a2da58 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/WithdrawRequest.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/WithdrawRequest.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.net.intent.impl;
 
-import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentData;
 
 import java.util.Optional;
@@ -26,17 +25,20 @@
 
     // TODO: define an interface and use it, instead of IntentManager
     private final IntentManager intentManager;
-    private final Intent intent;
-    private final IntentData currentState;
+    private final IntentData pending;
 
-    WithdrawRequest(IntentManager intentManager, Intent intent, IntentData currentState) {
+    WithdrawRequest(IntentManager intentManager, IntentData intentData) {
         this.intentManager = checkNotNull(intentManager);
-        this.intent = checkNotNull(intent);
-        this.currentState = currentState;
+        this.pending = checkNotNull(intentData);
     }
 
     @Override
     public Optional<IntentUpdate> execute() {
-        return Optional.of(new Withdrawing(intentManager, intent, currentState.installables())); //FIXME
+        //FIXME... store hack
+        IntentData current = intentManager.store.getIntentData(pending.key());
+        //TODO perhaps we want to validate that the pending and current are the
+        // same version i.e. they are the same
+        // Note: this call is not just the symmetric version of submit
+        return Optional.of(new Withdrawing(intentManager, pending, current));
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawing.java b/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawing.java
index c1da0ad..de91bc2 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawing.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawing.java
@@ -15,11 +15,9 @@
  */
 package org.onosproject.net.intent.impl;
 
-import com.google.common.collect.ImmutableList;
-import org.onosproject.net.flow.FlowRuleBatchOperation;
-import org.onosproject.net.intent.Intent;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.intent.IntentData;
 
-import java.util.List;
 import java.util.Optional;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -28,19 +26,21 @@
 
     // TODO: define an interface and use it, instead of IntentManager
     private final IntentManager intentManager;
-    private final Intent intent;
-    private final List<Intent> installables;
+    private final IntentData pending;
+    private final IntentData current;
 
-    Withdrawing(IntentManager intentManager, Intent intent, List<Intent> installables) {
+    Withdrawing(IntentManager intentManager, IntentData pending, IntentData current) {
         this.intentManager = checkNotNull(intentManager);
-        this.intent = checkNotNull(intent);
-        this.installables = ImmutableList.copyOf(installables);
+        this.pending = checkNotNull(pending);
+        this.current = checkNotNull(current);
     }
 
     @Override
     public Optional<IntentUpdate> execute() {
-        List<FlowRuleBatchOperation> batches = intentManager.uninstallIntent(intent, installables);
+        FlowRuleOperations flowRules
+                = intentManager.uninstallIntent(current.intent(), current.installables());
+        intentManager.flowRuleService.apply(flowRules); //FIXME
 
-        return Optional.of(new Withdrawn(intentManager, intent, installables, batches));
+        return Optional.of(new Withdrawn(pending));
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawn.java b/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawn.java
index a313fea..6ea20ae 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawn.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/Withdrawn.java
@@ -15,53 +15,22 @@
  */
 package org.onosproject.net.intent.impl;
 
-import com.google.common.collect.ImmutableList;
-import org.onosproject.net.flow.FlowRuleBatchOperation;
-import org.onosproject.net.intent.Intent;
-
-import java.util.LinkedList;
-import java.util.List;
+import org.onosproject.net.intent.IntentData;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.WITHDRAWING;
 
 class Withdrawn implements CompletedIntentUpdate {
 
-    // TODO: define an interface and use it, instead of IntentManager
-    private final IntentManager intentManager;
-    private final Intent intent;
-    private final List<Intent> installables;
-    private final List<FlowRuleBatchOperation> batches;
-    private int currentBatch;
+    private final IntentData intentData;
 
-    Withdrawn(IntentManager intentManager,
-              Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
-        this.intentManager = checkNotNull(intentManager);
-        this.intent = checkNotNull(intent);
-        this.installables = ImmutableList.copyOf(installables);
-        this.batches = new LinkedList<>(batches);
-        this.currentBatch = 0;
+    Withdrawn(IntentData intentData) {
+        this.intentData = checkNotNull(intentData);
+        this.intentData.setState(WITHDRAWING);
     }
 
     @Override
-    public List<Intent> allInstallables() {
-        return installables;
-    }
-
-    @Override
-    public void batchSuccess() {
-        currentBatch++;
-    }
-
-    @Override
-    public FlowRuleBatchOperation currentBatch() {
-        return currentBatch < batches.size() ? batches.get(currentBatch) : null;
-    }
-
-    @Override
-    public void batchFailed() {
-        for (int i = batches.size() - 1; i >= currentBatch; i--) {
-            batches.remove(i);
-        }
-        batches.addAll(intentManager.uninstallIntent(intent, installables));
+    public IntentData data() {
+        return intentData;
     }
 }