Intent F/W improvements

 - aggregate installables into FlowRuleOperations
 - added some impl. to SimpleIntentStore
 - created Coordinating State

Change-Id: I5b26ec1fdb7aaff9d5da4f21b2d5a249568ac5ac
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 50c609a..b260080 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,16 +15,14 @@
  */
 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;
 
 /**
  * Abstraction of entity capable of installing intents to the environment.
  */
+//TODO consider refactoring this API
 public interface IntentInstaller<T extends Intent> {
     /**
      * Installs the specified intent to the environment.
@@ -33,32 +31,7 @@
      * @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.
@@ -67,32 +40,7 @@
      * @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.
@@ -104,29 +52,5 @@
      */
     @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 e784621..eef8d0b 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
@@ -46,7 +46,7 @@
         try {
             List<Intent> installables = (current != null) ? current.installables() : null;
             pending.setInstallables(intentManager.compileIntent(pending.intent(), installables));
-            return Optional.of(new Installing(intentManager, pending, current));
+            return Optional.of(new Coordinating(intentManager, pending, current));
         } catch (PathNotFoundException e) {
             log.debug("Path not found for intent {}", pending.intent());
             // TODO: revisit to implement failure handling
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/Coordinating.java b/core/net/src/main/java/org/onosproject/net/intent/impl/Coordinating.java
new file mode 100644
index 0000000..3682dbf
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/Coordinating.java
@@ -0,0 +1,53 @@
+/*
+ * 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.flow.FlowRuleOperations;
+import org.onosproject.net.intent.IntentData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+// TODO: better naming because install() method actually generate FlowRuleBatchOperations
+class Coordinating implements IntentUpdate {
+
+    private static final Logger log = LoggerFactory.getLogger(Coordinating.class);
+
+    private final IntentManager intentManager;
+    private final IntentData pending;
+    private final IntentData current;
+
+    // TODO: define an interface and use it, instead of IntentManager
+    Coordinating(IntentManager intentManager, IntentData pending, IntentData current) {
+        this.intentManager = checkNotNull(intentManager);
+        this.pending = checkNotNull(pending);
+        this.current = current;
+    }
+
+    @Override
+    public Optional<IntentUpdate> execute() {
+        try {
+            FlowRuleOperations flowRules = intentManager.coordinate(pending);
+            return Optional.of(new Installing(intentManager, pending, flowRules));
+        } catch (FlowRuleBatchOperationConversionException e) {
+            log.warn("Unable to install intent {} due to:", pending.intent().id(), e.getCause());
+            return Optional.of(new InstallingFailed(pending)); //FIXME
+        }
+    }
+}
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 0d9ce36..c8d0294 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
@@ -31,22 +31,19 @@
 
     private final IntentManager intentManager;
     private final IntentData pending;
-    private final IntentData current;
+    private final FlowRuleOperations flowRules;
 
     // TODO: define an interface and use it, instead of IntentManager
-    Installing(IntentManager intentManager, IntentData pending, IntentData current) {
+    Installing(IntentManager intentManager, IntentData pending, FlowRuleOperations flowRules) {
         this.intentManager = checkNotNull(intentManager);
         this.pending = checkNotNull(pending);
-        this.current = current;
+        this.flowRules = flowRules;
     }
 
     @Override
     public Optional<IntentUpdate> execute() {
         try {
-            FlowRuleOperations flowRules = intentManager.coordinate(pending.installables());
-            // TODO: call FlowRuleService API to push FlowRules and track resources,
-            // which the submitted intent will use.
-            intentManager.flowRuleService.apply(flowRules);
+            intentManager.flowRuleService.apply(flowRules); // FIXME we need to provide a context
             return Optional.of(new Installed(pending));
         } catch (FlowRuleBatchOperationConversionException e) {
             log.warn("Unable to install intent {} due to:", pending.intent().id(), e.getCause());
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 086578c..65da61a 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
@@ -28,8 +28,13 @@
 import org.onosproject.core.IdGenerator;
 import org.onosproject.event.AbstractListenerRegistry;
 import org.onosproject.event.EventDeliveryService;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleBatchEntry;
 import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
 import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
 import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentBatchDelegate;
@@ -50,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -281,19 +287,74 @@
 
     //TODO javadoc
     //FIXME
-    FlowRuleOperations coordinate(List<Intent> installables) {
-        //List<FlowRuleBatchOperation> batches = new ArrayList<>(installables.size());
+    FlowRuleOperations coordinate(IntentData pending) {
+        List<Intent> installables = pending.installables();
+        List<List<FlowRuleBatchOperation>> plans = 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*/);
+                plans.add(getInstaller(installable).install(installable));
             } catch (Exception e) { // TODO this should be IntentException
                 throw new FlowRuleBatchOperationConversionException(null/*FIXME*/, e);
             }
         }
-        return null;
+
+        return merge(plans).build(new FlowRuleOperationsContext() { // FIXME move this out
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                log.info("Completed installing: {}", pending.key());
+                pending.setState(INSTALLED);
+                store.write(pending);
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                //FIXME store.write(pending.setState(BROKEN));
+            }
+        });
+    }
+
+    // FIXME... needs tests... or maybe it's just perfect
+    private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
+        FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+        // Build a batch one stage at a time
+        for (int stageNumber = 0;; stageNumber++) {
+            // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
+            for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
+                List<FlowRuleBatchOperation> plan = itr.next();
+                if (plan.size() <= stageNumber) {
+                    // we have consumed all stages from this plan, so remove it
+                    itr.remove();
+                    continue;
+                }
+                // write operations from this sub-stage into the builder
+                FlowRuleBatchOperation stage = plan.get(stageNumber);
+                for (FlowRuleBatchEntry entry : stage.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;
+                    }
+                }
+            }
+            // we are done with the stage, start the next one...
+            if (plans.isEmpty()) {
+                break; // we don't need to start a new stage, we are done.
+            }
+            builder.newStage();
+        }
+        return builder;
     }
 
     /**
@@ -311,7 +372,7 @@
                     installable.resources());
             try {
                 // FIXME need to aggregate the FlowRuleOperations across installables
-                getInstaller(installable).uninstall2(installable).build(null/*FIXME*/);
+                getInstaller(installable).uninstall(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?
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleIntentStore.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleIntentStore.java
index 6658eff..988d02a 100644
--- a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleIntentStore.java
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleIntentStore.java
@@ -25,6 +25,8 @@
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.IntentStore;
 import org.onosproject.net.intent.IntentStoreDelegate;
 import org.onosproject.net.intent.Key;
@@ -33,6 +35,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -73,6 +76,36 @@
     }
 
     @Override
+    public Intent getIntent(IntentId intentId) {
+        for (IntentData data : current.values()) {
+            if (Objects.equals(data.intent().id(), intentId)) {
+                return data.intent();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public IntentState getIntentState(IntentId intentId) {
+        for (IntentData data : current.values()) {
+            if (Objects.equals(data.intent().id(), intentId)) {
+                return data.state();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public List<Intent> getInstallableIntents(IntentId intentId) {
+        for (IntentData data : current.values()) {
+            if (Objects.equals(data.intent().id(), intentId)) {
+                return data.installables();
+            }
+        }
+        return null;
+    }
+
+    @Override
     public IntentData getIntentData(Key key) {
         return current.get(key);
     }
@@ -164,6 +197,12 @@
         }
     }
 
+    @Override
+    public Intent getIntent(Key key) {
+        IntentData data = current.get(key);
+        return (data != null) ? data.intent() : null;
+    }
+
 
     @Override
     public void addPending(IntentData data) {