ONOS-1048 - Define interfaces instead of using IntentManager

- Define IntentProcessor interface containing methods to process an intent
- Pull out IntentCompiler related tasks to CompilerRegistry
- Pull out IntentInstaller related tasks to InstallerRegistry
- Create an IntentProcessor subclass as inner class in IntentManager

Change-Id: Ia3e8d574a1053e7ddc9b961873ef758c9e0b1b26
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/CompilerRegistry.java b/core/net/src/main/java/org/onosproject/net/intent/impl/CompilerRegistry.java
new file mode 100644
index 0000000..0ee149a
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/CompilerRegistry.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+// TODO: consider a better name
+class CompilerRegistry {
+
+    private final ConcurrentMap<Class<? extends Intent>,
+            IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
+
+    /**
+     * Registers the specified compiler for the given intent class.
+     *
+     * @param cls      intent class
+     * @param compiler intent compiler
+     * @param <T>      the type of intent
+     */
+    public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+        compilers.put(cls, compiler);
+    }
+
+    /**
+     * Unregisters the compiler for the specified intent class.
+     *
+     * @param cls intent class
+     * @param <T> the type of intent
+     */
+    public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+        compilers.remove(cls);
+    }
+
+    /**
+     * Returns immutable set of bindings of currently registered intent compilers.
+     *
+     * @return the set of compiler bindings
+     */
+    public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
+        return ImmutableMap.copyOf(compilers);
+    }
+
+    /**
+     * Compiles an intent recursively.
+     *
+     * @param intent intent
+     * @param previousInstallables previous intent installables
+     * @return result of compilation
+     */
+    List<Intent> compile(Intent intent, List<Intent> previousInstallables) {
+        if (intent.isInstallable()) {
+            return ImmutableList.of(intent);
+        }
+
+        registerSubclassCompilerIfNeeded(intent);
+        // FIXME: get previous resources
+        List<Intent> installable = new ArrayList<>();
+        for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
+            installable.addAll(compile(compiled, previousInstallables));
+        }
+        return installable;
+    }
+
+    /**
+     * Returns the corresponding intent compiler to the specified intent.
+     *
+     * @param intent intent
+     * @param <T>    the type of intent
+     * @return intent compiler corresponding to the specified intent
+     */
+    private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
+        @SuppressWarnings("unchecked")
+        IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
+        if (compiler == null) {
+            throw new IntentException("no compiler for class " + intent.getClass());
+        }
+        return compiler;
+    }
+
+    /**
+     * Registers an intent compiler of the specified intent if an intent compiler
+     * for the intent is not registered. This method traverses the class hierarchy of
+     * the intent. Once an intent compiler for a parent type is found, this method
+     * registers the found intent compiler.
+     *
+     * @param intent intent
+     */
+    private void registerSubclassCompilerIfNeeded(Intent intent) {
+        if (!compilers.containsKey(intent.getClass())) {
+            Class<?> cls = intent.getClass();
+            while (cls != Object.class) {
+                // As long as we're within the Intent class descendants
+                if (Intent.class.isAssignableFrom(cls)) {
+                    IntentCompiler<?> compiler = compilers.get(cls);
+                    if (compiler != null) {
+                        compilers.put(intent.getClass(), compiler);
+                        return;
+                    }
+                }
+                cls = cls.getSuperclass();
+            }
+        }
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/InstallerRegistry.java b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallerRegistry.java
new file mode 100644
index 0000000..e0103ce
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallerRegistry.java
@@ -0,0 +1,254 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import org.onosproject.net.flow.FlowRuleOperation;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentException;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.IntentStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.onlab.util.Tools.isNullOrEmpty;
+import static org.onosproject.net.intent.IntentState.FAILED;
+import static org.onosproject.net.intent.IntentState.INSTALLED;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+
+// TODO: consider a better name
+class InstallerRegistry {
+
+    private static final Logger log = LoggerFactory.getLogger(InstallerRegistry.class);
+
+    private final ConcurrentMap<Class<? extends Intent>,
+            IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
+    /**
+     * Registers the specified installer for the given installable intent class.
+     *
+     * @param cls       installable intent class
+     * @param installer intent installer
+     * @param <T>       the type of installable intent
+     */
+    <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+        installers.put(cls, installer);
+    }
+
+    /**
+     * Unregisters the installer for the given installable intent class.
+     *
+     * @param cls installable intent class
+     * @param <T> the type of installable intent
+     */
+    <T extends Intent> void unregisterInstaller(Class<T> cls) {
+        installers.remove(cls);
+    }
+
+    /**
+     * Returns immutable set of bindings of currently registered intent installers.
+     *
+     * @return the set of installer bindings
+     */
+    Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
+        return ImmutableMap.copyOf(installers);
+    }
+
+    /**
+     * Returns the corresponding intent installer to the specified installable intent.
+     *
+     * @param intent intent
+     * @param <T>    the type of installable intent
+     * @return intent installer corresponding to the specified installable intent
+     */
+    private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
+        @SuppressWarnings("unchecked")
+        IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
+        if (installer == null) {
+            throw new IntentException("no installer for class " + intent.getClass());
+        }
+        return installer;
+    }
+
+    /**
+     * Registers an intent installer of the specified intent if an intent installer
+     * for the intent is not registered. This method traverses the class hierarchy of
+     * the intent. Once an intent installer for a parent type is found, this method
+     * registers the found intent installer.
+     *
+     * @param intent intent
+     */
+    private void registerSubclassInstallerIfNeeded(Intent intent) {
+        if (!installers.containsKey(intent.getClass())) {
+            Class<?> cls = intent.getClass();
+            while (cls != Object.class) {
+                // As long as we're within the Intent class descendants
+                if (Intent.class.isAssignableFrom(cls)) {
+                    IntentInstaller<?> installer = installers.get(cls);
+                    if (installer != null) {
+                        installers.put(intent.getClass(), installer);
+                        return;
+                    }
+                }
+                cls = cls.getSuperclass();
+            }
+        }
+    }
+
+    /**
+     * Generate a {@link FlowRuleOperations} instance from the specified intent data.
+     *
+     * @param current        intent data stored in the store
+     * @param pending        intent data being processed
+     * @param store          intent store saving the intent state in this method
+     * @param trackerService objective tracker that is used in this method
+     * @return flow rule operations
+     */
+    public FlowRuleOperations coordinate(IntentData current, IntentData pending,
+                                         IntentStore store, ObjectiveTrackerService trackerService) {
+        List<Intent> oldInstallables = (current != null) ? current.installables() : null;
+        List<Intent> newInstallables = pending.installables();
+
+        checkState(isNullOrEmpty(oldInstallables) ||
+                        oldInstallables.size() == newInstallables.size(),
+                "Old and New Intent must have equivalent installable intents.");
+
+        List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
+        for (int i = 0; i < newInstallables.size(); i++) {
+            Intent newInstallable = newInstallables.get(i);
+            registerSubclassInstallerIfNeeded(newInstallable);
+            //TODO consider migrating installers to FlowRuleOperations
+            /* FIXME
+               - we need to do another pass on this method about that doesn't
+               require the length of installables to be equal, and also doesn't
+               depend on ordering
+               - we should also reconsider when to start/stop tracking resources
+             */
+            if (isNullOrEmpty(oldInstallables)) {
+                plans.add(getInstaller(newInstallable).install(newInstallable));
+            } else {
+                Intent oldInstallable = oldInstallables.get(i);
+                checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
+                        "Installable Intent type mismatch.");
+                trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
+                plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
+            }
+            trackerService.addTrackedResources(pending.key(), newInstallable.resources());
+//            } catch (IntentException e) {
+//                log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
+//                //FIXME... we failed. need to uninstall (if same) or revert (if different)
+//                trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
+//                exception = e;
+//                batches = uninstallIntent(oldIntent, oldInstallables);
+//            }
+        }
+
+        return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                log.debug("Completed installing: {}", pending.key());
+                pending.setState(INSTALLED);
+                store.write(pending);
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.warn("Failed installation: {} {} on {}", pending.key(),
+                        pending.intent(), ops);
+                //TODO store.write(pending.setState(BROKEN));
+                pending.setState(FAILED);
+                store.write(pending);
+            }
+        });
+    }
+
+    /**
+     * Generate a {@link FlowRuleOperations} instance from the specified intent data.
+     *
+     * @param current        intent data stored in the store
+     * @param pending        intent date being processed
+     * @param store          intent store saving the intent state in this method
+     * @param trackerService objective tracker that is used in this method
+     * @return flow rule operations
+     */
+    FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending,
+                                                  IntentStore store, ObjectiveTrackerService trackerService) {
+        List<Intent> installables = current.installables();
+        List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
+        for (Intent installable : installables) {
+            plans.add(getInstaller(installable).uninstall(installable));
+            trackerService.removeTrackedResources(pending.key(), installable.resources());
+        }
+
+        return merge(plans).build(new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                log.debug("Completed withdrawing: {}", pending.key());
+                pending.setState(WITHDRAWN);
+                pending.setInstallables(Collections.emptyList());
+                store.write(pending);
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.warn("Failed withdraw: {}", pending.key());
+                pending.setState(FAILED);
+                store.write(pending);
+            }
+        });
+    }
+
+
+    // TODO needs tests... or maybe it's just perfect
+    private FlowRuleOperations.Builder merge(List<List<Collection<FlowRuleOperation>>> 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<Set<FlowRuleOperation>)
+            for (Iterator<List<Collection<FlowRuleOperation>>> itr = plans.iterator(); itr.hasNext();) {
+                List<Collection<FlowRuleOperation>> 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
+                Collection<FlowRuleOperation> stage = plan.get(stageNumber);
+                for (FlowRuleOperation entry : stage) {
+                    builder.operation(entry);
+                }
+            }
+            // 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;
+    }
+}
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 8fa8b3f..3699df6 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
@@ -16,7 +16,6 @@
 package org.onosproject.net.intent.impl;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -27,16 +26,13 @@
 import org.onosproject.core.IdGenerator;
 import org.onosproject.event.AbstractListenerRegistry;
 import org.onosproject.event.EventDeliveryService;
-import org.onosproject.net.flow.FlowRuleOperation;
 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;
 import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentEvent;
-import org.onosproject.net.intent.IntentException;
 import org.onosproject.net.intent.IntentExtensionService;
 import org.onosproject.net.intent.IntentInstaller;
 import org.onosproject.net.intent.IntentListener;
@@ -53,29 +49,26 @@
 import org.onosproject.net.intent.impl.phase.Withdrawn;
 import org.slf4j.Logger;
 
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.EnumSet;
-import java.util.Iterator;
 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 com.google.common.base.Preconditions.checkState;
 import static java.util.concurrent.Executors.newFixedThreadPool;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onlab.util.Tools.isNullOrEmpty;
-import static org.onosproject.net.intent.IntentState.*;
+import static org.onosproject.net.intent.IntentState.FAILED;
+import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -95,12 +88,6 @@
     private static final EnumSet<IntentState> RECOMPILE
             = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
 
-    // Collections for compiler, installer, and listener are ONOS instance local
-    private final ConcurrentMap<Class<? extends Intent>,
-            IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
-    private final ConcurrentMap<Class<? extends Intent>,
-            IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
-
     private final AbstractListenerRegistry<IntentEvent, IntentListener>
             listenerRegistry = new AbstractListenerRegistry<>();
 
@@ -124,6 +111,9 @@
     private ExecutorService batchExecutor;
     private ExecutorService workerExecutor;
 
+    private final CompilerRegistry compilerRegistry = new CompilerRegistry();
+    private final InstallerRegistry installerRegistry = new InstallerRegistry();
+    private final InternalIntentProcessor processor = new InternalIntentProcessor();
     private final IntentStoreDelegate delegate = new InternalStoreDelegate();
     private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
     private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
@@ -211,259 +201,32 @@
 
     @Override
     public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
-        compilers.put(cls, compiler);
+        compilerRegistry.registerCompiler(cls, compiler);
     }
 
     @Override
     public <T extends Intent> void unregisterCompiler(Class<T> cls) {
-        compilers.remove(cls);
+        compilerRegistry.unregisterCompiler(cls);
     }
 
     @Override
     public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
-        return ImmutableMap.copyOf(compilers);
+        return compilerRegistry.getCompilers();
     }
 
     @Override
     public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
-        installers.put(cls, installer);
+        installerRegistry.registerInstaller(cls, installer);
     }
 
     @Override
     public <T extends Intent> void unregisterInstaller(Class<T> cls) {
-        installers.remove(cls);
+        installerRegistry.unregisterInstaller(cls);
     }
 
     @Override
     public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
-        return ImmutableMap.copyOf(installers);
-    }
-
-    /**
-     * Returns the corresponding intent compiler to the specified intent.
-     *
-     * @param intent intent
-     * @param <T>    the type of intent
-     * @return intent compiler corresponding to the specified intent
-     */
-    private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
-        @SuppressWarnings("unchecked")
-        IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
-        if (compiler == null) {
-            throw new IntentException("no compiler for class " + intent.getClass());
-        }
-        return compiler;
-    }
-
-    /**
-     * Returns the corresponding intent installer to the specified installable intent.
-     *
-     * @param intent intent
-     * @param <T>    the type of installable intent
-     * @return intent installer corresponding to the specified installable intent
-     */
-    private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
-        @SuppressWarnings("unchecked")
-        IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
-        if (installer == null) {
-            throw new IntentException("no installer for class " + intent.getClass());
-        }
-        return installer;
-    }
-
-    /**
-     * Compiles an intent recursively.
-     *
-     * @param intent intent
-     * @param previousInstallables previous intent installables
-     * @return result of compilation
-     */
-    // TODO: make this non-public due to short term hack for ONOS-1051
-    public List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
-        if (intent.isInstallable()) {
-            return ImmutableList.of(intent);
-        }
-
-        registerSubclassCompilerIfNeeded(intent);
-        // FIXME: get previous resources
-        List<Intent> installable = new ArrayList<>();
-        for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
-            installable.addAll(compileIntent(compiled, previousInstallables));
-        }
-        return installable;
-    }
-
-    //TODO javadoc
-    //FIXME
-    // TODO: make this non-public due to short term hack for ONOS-1051
-    public FlowRuleOperations coordinate(IntentData current, IntentData pending) {
-        List<Intent> oldInstallables = (current != null) ? current.installables() : null;
-        List<Intent> newInstallables = pending.installables();
-
-        checkState(isNullOrEmpty(oldInstallables) ||
-                   oldInstallables.size() == newInstallables.size(),
-                   "Old and New Intent must have equivalent installable intents.");
-
-        List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
-        for (int i = 0; i < newInstallables.size(); i++) {
-            Intent newInstallable = newInstallables.get(i);
-            registerSubclassInstallerIfNeeded(newInstallable);
-            //TODO consider migrating installers to FlowRuleOperations
-            /* FIXME
-               - we need to do another pass on this method about that doesn't
-               require the length of installables to be equal, and also doesn't
-               depend on ordering
-               - we should also reconsider when to start/stop tracking resources
-             */
-            if (isNullOrEmpty(oldInstallables)) {
-                plans.add(getInstaller(newInstallable).install(newInstallable));
-            } else {
-                Intent oldInstallable = oldInstallables.get(i);
-                checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
-                           "Installable Intent type mismatch.");
-                trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
-                plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
-            }
-            trackerService.addTrackedResources(pending.key(), newInstallable.resources());
-//            } catch (IntentException e) {
-//                log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
-//                //FIXME... we failed. need to uninstall (if same) or revert (if different)
-//                trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
-//                exception = e;
-//                batches = uninstallIntent(oldIntent, oldInstallables);
-//            }
-        }
-
-        return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
-            @Override
-            public void onSuccess(FlowRuleOperations ops) {
-                log.debug("Completed installing: {}", pending.key());
-                pending.setState(INSTALLED);
-                store.write(pending);
-            }
-
-            @Override
-            public void onError(FlowRuleOperations ops) {
-                log.warn("Failed installation: {} {} on {}", pending.key(),
-                         pending.intent(), ops);
-                //TODO store.write(pending.setState(BROKEN));
-                pending.setState(FAILED);
-                store.write(pending);
-            }
-        });
-    }
-
-    /**
-     * Generate a {@link FlowRuleOperations} instance from the specified intent data.
-     *
-     * @param current intent data stored in the store
-     * @param pending intent data that is pending
-     * @return flow rule operations
-     */
-    // TODO: make this non-public due to short term hack for ONOS-1051
-    public FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
-        List<Intent> installables = current.installables();
-        List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
-        for (Intent installable : installables) {
-            plans.add(getInstaller(installable).uninstall(installable));
-            trackerService.removeTrackedResources(pending.key(), installable.resources());
-        }
-
-        return merge(plans).build(new FlowRuleOperationsContext() {
-            @Override
-            public void onSuccess(FlowRuleOperations ops) {
-                log.debug("Completed withdrawing: {}", pending.key());
-                pending.setState(WITHDRAWN);
-                pending.setInstallables(Collections.emptyList());
-                store.write(pending);
-            }
-
-            @Override
-            public void onError(FlowRuleOperations ops) {
-                log.warn("Failed withdraw: {}", pending.key());
-                pending.setState(FAILED);
-                store.write(pending);
-            }
-        });
-    }
-
-
-    // TODO needs tests... or maybe it's just perfect
-    private FlowRuleOperations.Builder merge(List<List<Collection<FlowRuleOperation>>> 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<Set<FlowRuleOperation>)
-            for (Iterator<List<Collection<FlowRuleOperation>>> itr = plans.iterator(); itr.hasNext();) {
-                List<Collection<FlowRuleOperation>> 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
-                Collection<FlowRuleOperation> stage = plan.get(stageNumber);
-                for (FlowRuleOperation entry : stage) {
-                    builder.operation(entry);
-                }
-            }
-            // 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;
-    }
-
-    /**
-     * Registers an intent compiler of the specified intent if an intent compiler
-     * for the intent is not registered. This method traverses the class hierarchy of
-     * the intent. Once an intent compiler for a parent type is found, this method
-     * registers the found intent compiler.
-     *
-     * @param intent intent
-     */
-    private void registerSubclassCompilerIfNeeded(Intent intent) {
-        if (!compilers.containsKey(intent.getClass())) {
-            Class<?> cls = intent.getClass();
-            while (cls != Object.class) {
-                // As long as we're within the Intent class descendants
-                if (Intent.class.isAssignableFrom(cls)) {
-                    IntentCompiler<?> compiler = compilers.get(cls);
-                    if (compiler != null) {
-                        compilers.put(intent.getClass(), compiler);
-                        return;
-                    }
-                }
-                cls = cls.getSuperclass();
-            }
-        }
-    }
-
-    /**
-     * Registers an intent installer of the specified intent if an intent installer
-     * for the intent is not registered. This method traverses the class hierarchy of
-     * the intent. Once an intent installer for a parent type is found, this method
-     * registers the found intent installer.
-     *
-     * @param intent intent
-     */
-    private void registerSubclassInstallerIfNeeded(Intent intent) {
-        if (!installers.containsKey(intent.getClass())) {
-            Class<?> cls = intent.getClass();
-            while (cls != Object.class) {
-                // As long as we're within the Intent class descendants
-                if (Intent.class.isAssignableFrom(cls)) {
-                    IntentInstaller<?> installer = installers.get(cls);
-                    if (installer != null) {
-                        installers.put(intent.getClass(), installer);
-                        return;
-                    }
-                }
-                cls = cls.getSuperclass();
-            }
-        }
+        return installerRegistry.getInstallers();
     }
 
     // Store delegate to re-post events emitted from the store.
@@ -525,12 +288,12 @@
         IntentData current = store.getIntentData(intentData.key());
         switch (intentData.state()) {
             case INSTALL_REQ:
-                return new InstallRequest(this, intentData, Optional.ofNullable(current));
+                return new InstallRequest(processor, intentData, Optional.ofNullable(current));
             case WITHDRAW_REQ:
                 if (current == null || isNullOrEmpty(current.installables())) {
                     return new Withdrawn(intentData, WITHDRAWN);
                 } else {
-                    return new WithdrawRequest(this, intentData, current);
+                    return new WithdrawRequest(processor, intentData, current);
                 }
             default:
                 // illegal state
@@ -648,4 +411,26 @@
             // TODO ensure that only one batch is in flight at a time
         }
     }
+
+    private class InternalIntentProcessor implements IntentProcessor {
+        @Override
+        public List<Intent> compile(Intent intent, List<Intent> previousInstallables) {
+            return compilerRegistry.compile(intent, previousInstallables);
+        }
+
+        @Override
+        public FlowRuleOperations coordinate(IntentData current, IntentData pending) {
+            return installerRegistry.coordinate(current, pending, store, trackerService);
+        }
+
+        @Override
+        public FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
+            return installerRegistry.uninstallCoordinate(current, pending, store, trackerService);
+        }
+
+        @Override
+        public void applyFlowRules(FlowRuleOperations flowRules) {
+            flowRuleService.apply(flowRules);
+        }
+    }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentProcessor.java b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentProcessor.java
new file mode 100644
index 0000000..c613a8d
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentProcessor.java
@@ -0,0 +1,68 @@
+/*
+ * 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.Intent;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.List;
+
+/**
+ * A collection of methods to process an intent.
+ *
+ * This interface is public, but intended to be used only by IntentManager and
+ * IntentProcessPhase subclasses stored under phase package.
+ */
+public interface IntentProcessor {
+
+    /**
+     * Compiles an intent recursively.
+     *
+     * @param intent intent
+     * @param previousInstallables previous intent installables
+     * @return result of compilation
+     */
+    List<Intent> compile(Intent intent, List<Intent> previousInstallables);
+
+    /**
+     * Generate a {@link FlowRuleOperations} instance from the specified intent data.
+     *
+     * @param current intent data stored in the store
+     * @param pending intent data being processed
+     * @return flow rule operations
+     */
+    FlowRuleOperations coordinate(IntentData current, IntentData pending);
+
+    /**
+     * Generate a {@link FlowRuleOperations} instance from the specified intent data.
+     *
+     * @param current intent data stored in the store
+     * @param pending intent data being processed
+     * @return flow rule operations
+     */
+    FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending);
+
+    /**
+     * Applies a batch operation of FlowRules.
+     *
+     * @param flowRules batch operation to apply
+     */
+    // TODO: consider a better name
+    // This methods gives strangeness a bit because
+    // it doesn't receive/return intent related information
+    void applyFlowRules(FlowRuleOperations flowRules);
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java
index b68230a..97ea18e 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java
@@ -18,7 +18,7 @@
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentException;
-import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.IntentProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,13 +34,12 @@
 
     private static final Logger log = LoggerFactory.getLogger(Compiling.class);
 
-    // TODO: define an interface and use it, instead of IntentManager
-    private final IntentManager intentManager;
+    private final IntentProcessor processor;
     private final IntentData pending;
     private final IntentData current;
 
-    Compiling(IntentManager intentManager, IntentData pending, IntentData current) {
-        this.intentManager = checkNotNull(intentManager);
+    Compiling(IntentProcessor processor, IntentData pending, IntentData current) {
+        this.processor = checkNotNull(processor);
         this.pending = checkNotNull(pending);
         this.current = current;
     }
@@ -49,11 +48,12 @@
     public Optional<IntentProcessPhase> execute() {
         try {
             List<Intent> installables = (current != null) ? current.installables() : null;
-            pending.setInstallables(intentManager.compileIntent(pending.intent(), installables));
-            return Optional.of(new InstallCoordinating(intentManager, pending, current));
+            pending.setInstallables(processor.compile(pending.intent(), installables));
+            return Optional.of(new InstallCoordinating(processor, pending, current));
         } catch (IntentException e) {
             log.debug("Unable to compile intent {} due to: {}", pending.intent(), e);
             return Optional.of(new CompilingFailed(pending));
         }
     }
+
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallCoordinating.java b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallCoordinating.java
index 5fd82ec..ef76888 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallCoordinating.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallCoordinating.java
@@ -18,7 +18,7 @@
 import org.onosproject.net.flow.FlowRuleOperations;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentException;
-import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.IntentProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,13 +34,13 @@
 
     private static final Logger log = LoggerFactory.getLogger(InstallCoordinating.class);
 
-    private final IntentManager intentManager;
+    private final IntentProcessor processor;
     private final IntentData pending;
     private final IntentData current;
 
     // TODO: define an interface and use it, instead of IntentManager
-    InstallCoordinating(IntentManager intentManager, IntentData pending, IntentData current) {
-        this.intentManager = checkNotNull(intentManager);
+    InstallCoordinating(IntentProcessor processor, IntentData pending, IntentData current) {
+        this.processor = checkNotNull(processor);
         this.pending = checkNotNull(pending);
         this.current = current;
     }
@@ -50,8 +50,8 @@
         try {
             //FIXME we orphan flow rules that are currently on the data plane
             // ... should either reuse them or remove them
-            FlowRuleOperations flowRules = intentManager.coordinate(current, pending);
-            return Optional.of(new Installing(intentManager, pending, flowRules));
+            FlowRuleOperations flowRules = processor.coordinate(current, pending);
+            return Optional.of(new Installing(processor, pending, flowRules));
         } catch (IntentException e) {
             log.warn("Unable to generate a FlowRuleOperations from intent {} due to:", pending.intent().id(), e);
             return Optional.of(new InstallingFailed(pending));
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java
index e54b10a..90e0fd2 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java
@@ -16,7 +16,7 @@
 package org.onosproject.net.intent.impl.phase;
 
 import org.onosproject.net.intent.IntentData;
-import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.IntentProcessor;
 
 import java.util.Optional;
 
@@ -27,13 +27,12 @@
  */
 public final class InstallRequest implements IntentProcessPhase {
 
-    // TODO: define an interface and use it, instead of IntentManager
-    private final IntentManager intentManager;
+    private final IntentProcessor intentManager;
     private final IntentData pending;
     private final Optional<IntentData> current;
 
-    public InstallRequest(IntentManager intentManager, IntentData intentData, Optional<IntentData> current) {
-        this.intentManager = checkNotNull(intentManager);
+    public InstallRequest(IntentProcessor processor, IntentData intentData, Optional<IntentData> current) {
+        this.intentManager = checkNotNull(processor);
         this.pending = checkNotNull(intentData);
         this.current = checkNotNull(current);
     }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java
index 05c499f..3a290ae 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java
@@ -18,7 +18,7 @@
 import org.onosproject.net.flow.FlowRuleOperations;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentException;
-import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.IntentProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,13 +34,12 @@
 
     private static final Logger log = LoggerFactory.getLogger(Installing.class);
 
-    private final IntentManager intentManager;
+    private final IntentProcessor processor;
     private final IntentData pending;
     private final FlowRuleOperations flowRules;
 
-    // TODO: define an interface and use it, instead of IntentManager
-    Installing(IntentManager intentManager, IntentData pending, FlowRuleOperations flowRules) {
-        this.intentManager = checkNotNull(intentManager);
+    Installing(IntentProcessor processor, IntentData pending, FlowRuleOperations flowRules) {
+        this.processor = checkNotNull(processor);
         this.pending = checkNotNull(pending);
         this.flowRules = flowRules;
     }
@@ -48,7 +47,7 @@
     @Override
     public Optional<IntentProcessPhase> execute() {
         try {
-            intentManager.flowRuleService.apply(flowRules); // FIXME we need to provide a context
+            processor.applyFlowRules(flowRules);
             return Optional.of(new Installed(pending));
         // What kinds of exceptions are thrown by FlowRuleService.apply()?
         // Is IntentException a correct exception abstraction?
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawCoordinating.java b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawCoordinating.java
index 24d6dd5..ee854c8 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawCoordinating.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawCoordinating.java
@@ -18,7 +18,7 @@
 import org.onosproject.net.flow.FlowRuleOperations;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentException;
-import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.IntentProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,13 +34,12 @@
 
     private static final Logger log = LoggerFactory.getLogger(WithdrawCoordinating.class);
 
-    // TODO: define an interface and use it, instead of IntentManager
-    private final IntentManager intentManager;
+    private final IntentProcessor processor;
     private final IntentData pending;
     private final IntentData current;
 
-    WithdrawCoordinating(IntentManager intentManager, IntentData pending, IntentData current) {
-        this.intentManager = checkNotNull(intentManager);
+    WithdrawCoordinating(IntentProcessor processor, IntentData pending, IntentData current) {
+        this.processor = checkNotNull(processor);
         this.pending = checkNotNull(pending);
         this.current = checkNotNull(current);
     }
@@ -49,9 +48,9 @@
     public Optional<IntentProcessPhase> execute() {
         try {
             // Note: current.installables() are not null or empty due to createIntentUpdate check
-            FlowRuleOperations flowRules = intentManager.uninstallCoordinate(current, pending);
+            FlowRuleOperations flowRules = processor.uninstallCoordinate(current, pending);
             pending.setInstallables(current.installables());
-            return Optional.of(new Withdrawing(intentManager, pending, flowRules));
+            return Optional.of(new Withdrawing(processor, pending, flowRules));
         } catch (IntentException e) {
             log.warn("Unable to generate generate a FlowRuleOperations from intent {} due to:", pending.intent(), e);
             return Optional.of(new WithdrawingFailed(pending));
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java
index bbc7f34..f2e120f 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java
@@ -16,7 +16,7 @@
 package org.onosproject.net.intent.impl.phase;
 
 import org.onosproject.net.intent.IntentData;
-import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.IntentProcessor;
 
 import java.util.Optional;
 
@@ -27,13 +27,12 @@
  */
 public final class WithdrawRequest implements IntentProcessPhase {
 
-    // TODO: define an interface and use it, instead of IntentManager
-    private final IntentManager intentManager;
+    private final IntentProcessor processor;
     private final IntentData pending;
     private final IntentData current;
 
-    public WithdrawRequest(IntentManager intentManager, IntentData intentData, IntentData current) {
-        this.intentManager = checkNotNull(intentManager);
+    public WithdrawRequest(IntentProcessor processor, IntentData intentData, IntentData current) {
+        this.processor = checkNotNull(processor);
         this.pending = checkNotNull(intentData);
         this.current = checkNotNull(current);
     }
@@ -43,6 +42,6 @@
         //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 WithdrawCoordinating(intentManager, pending, current));
+        return Optional.of(new WithdrawCoordinating(processor, pending, current));
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java
index 676c3c7..9198d0e 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java
@@ -17,7 +17,7 @@
 
 import org.onosproject.net.flow.FlowRuleOperations;
 import org.onosproject.net.intent.IntentData;
-import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.IntentProcessor;
 
 import java.util.Optional;
 
@@ -29,20 +29,19 @@
  */
 class Withdrawing implements IntentProcessPhase {
 
-    // TODO: define an interface and use it, instead of IntentManager
-    private final IntentManager intentManager;
+    private final IntentProcessor processor;
     private final IntentData pending;
     private final FlowRuleOperations flowRules;
 
-    Withdrawing(IntentManager intentManager, IntentData pending, FlowRuleOperations flowRules) {
-        this.intentManager = checkNotNull(intentManager);
+    Withdrawing(IntentProcessor processor, IntentData pending, FlowRuleOperations flowRules) {
+        this.processor = checkNotNull(processor);
         this.pending = checkNotNull(pending);
         this.flowRules = checkNotNull(flowRules);
     }
 
     @Override
     public Optional<IntentProcessPhase> execute() {
-        intentManager.flowRuleService.apply(flowRules);
+        processor.applyFlowRules(flowRules);
         return Optional.of(new Withdrawn(pending));
     }
 }