[ONOS-6348] Intent installer redesign

Change-Id: I9ae2e8158dc1c686eaf848f330566f9dbb78405f
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentExtensionService.java b/core/api/src/main/java/org/onosproject/net/intent/IntentExtensionService.java
index b4d40c9..f36b356 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/IntentExtensionService.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentExtensionService.java
@@ -48,4 +48,37 @@
      * @return the set of compiler bindings
      */
     Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers();
+
+    /**
+     * Registers the specific installer for the given intent class.
+     *
+     * @param cls intent class
+     * @param installer intent installer
+     * @param <T> the type of intent
+     */
+     <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer);
+
+    /**
+     * Unregisters the installer for the specific intent class.
+     *
+     * @param cls intent class
+     * @param <T> the type of intent
+     */
+    <T extends Intent> void unregisterInstaller(Class<T> cls);
+
+    /**
+     * Returns immutable set of binding of currently registered intent installers.
+     *
+     * @return the set of installer bindings
+     */
+    Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers();
+
+    /**
+     * Returns the installer for specific installable intent.
+     *
+     * @param cls the type of intent
+     * @param <T> the type of intent
+     * @return the installer for specific installable intent
+     */
+    <T extends Intent> IntentInstaller<T> getInstaller(Class<T> cls);
 }
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentInstallCoordinator.java b/core/api/src/main/java/org/onosproject/net/intent/IntentInstallCoordinator.java
new file mode 100644
index 0000000..6ba0935
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentInstallCoordinator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-present 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;
+
+/**
+ * Contact point for installers to report progress of intent installation and to
+ * update intent-specific status and context appropriately.
+ */
+public interface IntentInstallCoordinator {
+    /**
+     * Handles success state for an Intent operation context.
+     *
+     * @param context the Intent operation context
+     */
+    void intentInstallSuccess(IntentOperationContext context);
+
+    /**
+     * Handles failed state for an Intent operation context.
+     *
+     * @param context the Intent operation context
+     */
+    void intentInstallFailed(IntentOperationContext context);
+}
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentInstallationContext.java b/core/api/src/main/java/org/onosproject/net/intent/IntentInstallationContext.java
new file mode 100644
index 0000000..3656c16
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentInstallationContext.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2017-present 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;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Installation context for a high level Intent.
+ * Records pending and error operation contexts of installable Intents for the
+ * high level Intent.
+ */
+public class IntentInstallationContext {
+    private IntentData toUninstall;
+    private IntentData toInstall;
+    private Set<IntentOperationContext> pendingContexts = Sets.newConcurrentHashSet();
+    private Set<IntentOperationContext> errorContexts = Sets.newConcurrentHashSet();
+
+    /**
+     * Creates an Intent installation context by given information.
+     *
+     * @param toUninstall the Intent to uninstall
+     * @param toInstall the Intent to install
+     */
+    public IntentInstallationContext(IntentData toUninstall, IntentData toInstall) {
+        this.toUninstall = toUninstall;
+        this.toInstall = toInstall;
+    }
+
+    /**
+     * Removes a pending operation context.
+     *
+     * @param context the operation context to be added
+     */
+    public void removePendingContext(IntentOperationContext context) {
+        this.pendingContexts.remove(context);
+    }
+
+    /**
+     * Adds a pending context.
+     *
+     * @param context the operation context to be added
+     */
+    public void addPendingContext(IntentOperationContext context) {
+        this.pendingContexts.add(context);
+    }
+
+    /**
+     * Adds an error context.
+     *
+     * @param context the error context to be added.
+     */
+    public void addErrorContext(IntentOperationContext context) {
+        this.errorContexts.add(context);
+    }
+
+    /**
+     * Retrieves the pending contexts.
+     *
+     * @return the pending contexts
+     */
+    public Set<IntentOperationContext> pendingContexts() {
+        return ImmutableSet.copyOf(pendingContexts);
+    }
+
+    /**
+     * Retrieves the error contexts.
+     *
+     * @return the error contexts
+     */
+    public Set<IntentOperationContext> errorContexts() {
+        return ImmutableSet.copyOf(errorContexts);
+    }
+
+    /**
+     * Check if pending context is empty.
+     *
+     * @return true if pending contexts is empty; false otherwise
+     */
+    public synchronized boolean isPendingContextsEmpty() {
+        return pendingContexts.isEmpty();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("pendingContexts", pendingContexts)
+                .add("errorContexts", errorContexts)
+                .toString();
+    }
+
+    /**
+     * Retrieves the Intent data which to be uninstalled.
+     *
+     * @return the Intent data; empty value if not exists
+     */
+    public Optional<IntentData> toUninstall() {
+        return Optional.ofNullable(toUninstall);
+    }
+
+    /**
+     * Retrieves the Intent data which to be installed.
+     *
+     * @return the Intent data; empty value if not exists
+     */
+    public Optional<IntentData> toInstall() {
+        return Optional.ofNullable(toInstall);
+    }
+
+}
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
new file mode 100644
index 0000000..ac41865
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentInstaller.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-present 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;
+
+/**
+ * Manage installation process for specific installable Intents.
+ */
+public interface IntentInstaller<T extends Intent> {
+
+    /**
+     * The installation direction.
+     */
+    enum Direction {
+        /**
+         * Direction for adding any installable objects(flows, configs...).
+         */
+        ADD,
+
+        /**
+         * Direction for removing any installable objects(flows, configs...).
+         */
+        REMOVE
+    }
+
+    /**
+     * Applies an Intent operation context.
+     *
+     * @param context the Intent operation context
+     */
+    void apply(IntentOperationContext<T> context);
+}
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentOperationContext.java b/core/api/src/main/java/org/onosproject/net/intent/IntentOperationContext.java
new file mode 100644
index 0000000..e36d7ec
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentOperationContext.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017-present 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;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Operation context for installable Intent.
+ *
+ * @param <T> the type of installable Intent
+ */
+public class IntentOperationContext<T extends Intent> {
+    private IntentInstallationContext intentInstallationContext;
+    private List<T> intentsToUninstall;
+    private List<T> intentsToInstall;
+
+    /**
+     * Creates an operation context.
+     *
+     * @param intentsToUninstall the Intents to uninstall
+     * @param intentsToInstall the Intents to install
+     * @param intentInstallationContext the high level Intent installation information
+     */
+    public IntentOperationContext(List<T> intentsToUninstall, List<T> intentsToInstall,
+                                  IntentInstallationContext intentInstallationContext) {
+        this.intentsToUninstall = Lists.newArrayList(intentsToUninstall);
+        this.intentsToInstall = Lists.newArrayList(intentsToInstall);
+        this.intentInstallationContext = intentInstallationContext;
+    }
+
+    /**
+     * Retrieves installable Intents to uninstall.
+     *
+     * @return the Intents to uninstall
+     */
+    public List<T> intentsToUninstall() {
+        return intentsToUninstall;
+    }
+
+    /**
+     * Retrieves installable Intents to install.
+     *
+     * @return the Intents to install
+     */
+    public List<T> intentsToInstall() {
+        return intentsToInstall;
+    }
+
+    /**
+     * Retrieves high level Intent installation information.
+     *
+     * @return the high level Intent installation information
+     */
+    public IntentInstallationContext intentInstallationContext() {
+        return intentInstallationContext;
+    }
+
+    /**
+     * Retrieves high level Intent data to uninstall.
+     *
+     * @return high level Intent data to uninstall
+     */
+    public Optional<IntentData> toUninstall() {
+        return intentInstallationContext.toUninstall();
+    }
+
+    /**
+     * Retrieves high level Intent data to install.
+     *
+     * @return high level Intent data to install
+     */
+    public Optional<IntentData> toInstall() {
+        return intentInstallationContext.toInstall();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof IntentOperationContext)) {
+            return false;
+        }
+
+        IntentOperationContext that = (IntentOperationContext) obj;
+        return Objects.equals(intentsToInstall, that.intentsToInstall) &&
+                Objects.equals(intentsToUninstall, that.intentsToUninstall) &&
+                Objects.equals(intentInstallationContext, intentInstallationContext);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(intentsToInstall,
+                            intentsToUninstall,
+                            intentInstallationContext);
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java b/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java
index 02e8975..b37d8c5 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.net.intent;
 
+import com.google.common.collect.ImmutableMap;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -37,6 +39,7 @@
     private final Set<IntentListener> listeners = new HashSet<>();
 
     private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
+    private final Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> installers = new HashMap<>();
 
     private final ExecutorService executor = Executors.newSingleThreadExecutor();
     private final List<IntentException> exceptions = new ArrayList<>();
@@ -247,6 +250,26 @@
         return Collections.unmodifiableMap(compilers);
     }
 
+    @Override
+    public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+        installers.put(cls, installer);
+    }
+
+    @Override
+    public <T extends Intent> void unregisterInstaller(Class<T> cls) {
+        installers.remove(cls);
+    }
+
+    @Override
+    public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
+        return ImmutableMap.copyOf(installers);
+    }
+
+    @Override
+    public <T extends Intent> IntentInstaller<T> getInstaller(Class<T> cls) {
+        return (IntentInstaller<T>) installers.get(cls);
+    }
+
     private void registerSubclassCompilerIfNeeded(Intent intent) {
         if (!compilers.containsKey(intent.getClass())) {
             Class<?> cls = intent.getClass();
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentExtensionServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/intent/IntentExtensionServiceAdapter.java
new file mode 100644
index 0000000..f9fe65b
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentExtensionServiceAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017-present 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;
+
+import java.util.Map;
+
+/**
+ * Test adapter for Intent extension service.
+ */
+public class IntentExtensionServiceAdapter implements IntentExtensionService {
+    @Override
+    public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+
+    }
+
+    @Override
+    public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+
+    }
+
+    @Override
+    public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
+        return null;
+    }
+
+    @Override
+    public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+
+    }
+
+    @Override
+    public <T extends Intent> void unregisterInstaller(Class<T> cls) {
+
+    }
+
+    @Override
+    public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
+        return null;
+    }
+
+    @Override
+    public <T extends Intent> IntentInstaller<T> getInstaller(Class<T> cls) {
+        return null;
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/InstallCoordinator.java b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallCoordinator.java
new file mode 100644
index 0000000..d5eb1fd
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallCoordinator.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2017-present 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.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.IntentStore;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.onosproject.net.intent.IntentState.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of IntentInstallCoordinator.
+ */
+public class InstallCoordinator {
+    private static final String INSTALLER_NOT_FOUND = "Intent installer not found, Intent: {}";
+    private final Logger log = getLogger(IntentManager.class);
+
+    private InstallerRegistry installerRegistry;
+    private IntentStore intentStore;
+
+    /**
+     * Creates an InstallCoordinator.
+     *
+     * @param installerRegistry the installer registry
+     * @param intentStore the Intent store
+     */
+    public InstallCoordinator(InstallerRegistry installerRegistry,
+                              IntentStore intentStore) {
+        this.installerRegistry = installerRegistry;
+        this.intentStore = intentStore;
+    }
+
+    /**
+     * Applies Intent data to be uninstalled and to be installed.
+     *
+     * @param toUninstall Intent data to be uninstalled
+     * @param toInstall Intent data to be installed
+     */
+    public void installIntents(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
+        // If no any Intents to be uninstalled or installed, ignore it.
+        if (!toUninstall.isPresent() && !toInstall.isPresent()) {
+            return;
+        }
+
+        // Classify installable Intents to different installers.
+        ArrayListMultimap<IntentInstaller, Intent> uninstallInstallers;
+        ArrayListMultimap<IntentInstaller, Intent> installInstallers;
+        Set<IntentInstaller> allInstallers = Sets.newHashSet();
+
+        if (toUninstall.isPresent()) {
+            uninstallInstallers = getInstallers(toUninstall.get());
+            allInstallers.addAll(uninstallInstallers.keySet());
+        } else {
+            uninstallInstallers = ArrayListMultimap.create();
+        }
+
+        if (toInstall.isPresent()) {
+            installInstallers = getInstallers(toInstall.get());
+            allInstallers.addAll(installInstallers.keySet());
+        } else {
+            installInstallers = ArrayListMultimap.create();
+        }
+
+        // Generates an installation context for the high level Intent.
+        IntentInstallationContext installationContext =
+                new IntentInstallationContext(toUninstall.orElse(null), toInstall.orElse(null));
+
+        //Generates different operation context for different installable Intents.
+        Map<IntentInstaller, IntentOperationContext> contexts = Maps.newHashMap();
+        allInstallers.forEach(installer -> {
+            List<Intent> intentsToUninstall = uninstallInstallers.get(installer);
+            List<Intent> intentsToInstall = installInstallers.get(installer);
+
+            // Connect context to high level installation context
+            IntentOperationContext context =
+                    new IntentOperationContext(intentsToUninstall, intentsToInstall,
+                                               installationContext);
+            installationContext.addPendingContext(context);
+            contexts.put(installer, context);
+        });
+
+        // Apply contexts to installers
+        contexts.forEach((installer, context) -> {
+            installer.apply(context);
+        });
+    }
+
+    /**
+     * Generates a mapping for installable Intents to installers.
+     *
+     * @param intentData the Intent data which contains installable Intents
+     * @return the mapping for installable Intents to installers
+     */
+    private ArrayListMultimap<IntentInstaller, Intent> getInstallers(IntentData intentData) {
+        ArrayListMultimap<IntentInstaller, Intent> intentInstallers = ArrayListMultimap.create();
+        intentData.installables().forEach(intent -> {
+            IntentInstaller installer = installerRegistry.getInstaller(intent.getClass());
+            if (installer != null) {
+                intentInstallers.put(installer, intent);
+            } else {
+                log.warn(INSTALLER_NOT_FOUND, intent);
+            }
+        });
+        return intentInstallers;
+    }
+
+    /**
+     * Handles success operation context.
+     *
+     * @param context the operation context
+     */
+    public void success(IntentOperationContext context) {
+        IntentInstallationContext intentInstallationContext =
+                context.intentInstallationContext();
+        intentInstallationContext.removePendingContext(context);
+
+        if (intentInstallationContext.isPendingContextsEmpty()) {
+            finish(intentInstallationContext);
+        }
+    }
+
+    /**
+     * Handles failed operation context.
+     *
+     * @param context the operation context
+     */
+    public void failed(IntentOperationContext context) {
+        IntentInstallationContext intentInstallationContext =
+                context.intentInstallationContext();
+        intentInstallationContext.addErrorContext(context);
+        intentInstallationContext.removePendingContext(context);
+
+        if (intentInstallationContext.isPendingContextsEmpty()) {
+            finish(intentInstallationContext);
+        }
+    }
+
+    /**
+     * Completed the installation context and update the Intent store.
+     *
+     * @param intentInstallationContext the installation context
+     */
+    private void finish(IntentInstallationContext intentInstallationContext) {
+        Set<IntentOperationContext> errCtxs = intentInstallationContext.errorContexts();
+        Optional<IntentData> toUninstall = intentInstallationContext.toUninstall();
+        Optional<IntentData> toInstall = intentInstallationContext.toInstall();
+
+        // Intent install success
+        if (errCtxs == null || errCtxs.isEmpty()) {
+            if (toInstall.isPresent()) {
+                IntentData installData = toInstall.get();
+                log.debug("Completed installing: {}", installData.key());
+                installData.setState(INSTALLED);
+                intentStore.write(installData);
+            } else if (toUninstall.isPresent()) {
+                IntentData uninstallData = toUninstall.get();
+                log.debug("Completed withdrawing: {}", uninstallData.key());
+                switch (uninstallData.request()) {
+                    case INSTALL_REQ:
+                        log.warn("{} was requested to withdraw during installation?",
+                                 uninstallData.intent());
+                        uninstallData.setState(FAILED);
+                        break;
+                    case WITHDRAW_REQ:
+                    default: //TODO "default" case should not happen
+                        uninstallData.setState(WITHDRAWN);
+                        break;
+                }
+                // Intent has been withdrawn; we can clear the installables
+                intentStore.write(new IntentData(uninstallData, Collections.emptyList()));
+            }
+        } else {
+            // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
+            if (toInstall.isPresent()) {
+                IntentData installData = toInstall.get();
+                installData.setState(CORRUPT);
+                installData.incrementErrorCount();
+                intentStore.write(installData);
+            }
+            // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
+            if (toUninstall.isPresent()) {
+                IntentData uninstallData = toUninstall.get();
+                uninstallData.setState(CORRUPT);
+                uninstallData.incrementErrorCount();
+                intentStore.write(uninstallData);
+            }
+        }
+    }
+}
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..db5becc
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/InstallerRegistry.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017-present 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 com.google.common.collect.Maps;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentInstaller;
+
+import java.util.Map;
+
+/**
+ * The local registry for Intent installer.
+ */
+public class InstallerRegistry {
+    private final Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> installers;
+
+    public InstallerRegistry() {
+        installers = Maps.newConcurrentMap();
+    }
+
+    /**
+     * Registers the specific installer for the given intent class.
+     *
+     * @param cls intent class
+     * @param installer intent installer
+     * @param <T> the type of intent
+     */
+    public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+        installers.put(cls, installer);
+    }
+
+    /**
+     * Unregisters the installer for the specific intent class.
+     *
+     * @param cls intent class
+     * @param <T> the type of intent
+     */
+    public <T extends Intent> void unregisterInstaller(Class<T> cls) {
+        installers.remove(cls);
+    }
+
+    /**
+     * Returns immutable set of binding of currently registered intent installers.
+     *
+     * @return the set of installer bindings
+     */
+    public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
+        return ImmutableMap.copyOf(installers);
+    }
+
+    /**
+     * Get an Intent installer by given Intent type.
+     *
+     * @param cls the Intent type
+     * @param <T> the Intent type
+     * @return the Intent installer of the Intent type if exists; null otherwise
+     */
+    public <T extends Intent> IntentInstaller<T> getInstaller(Class<T> cls) {
+        return (IntentInstaller<T>) installers.get(cls);
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstaller.java b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstaller.java
deleted file mode 100644
index d0928ce..0000000
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstaller.java
+++ /dev/null
@@ -1,1051 +0,0 @@
-/*
- * Copyright 2016-present 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.ImmutableSet;
-import com.google.common.annotations.Beta;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import org.apache.commons.lang3.tuple.Pair;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
-import org.onosproject.net.behaviour.protection.ProtectionConfig;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.domain.DomainIntent;
-import org.onosproject.net.domain.DomainIntentOperations;
-import org.onosproject.net.domain.DomainIntentOperationsContext;
-import org.onosproject.net.domain.DomainIntentService;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.FlowRuleOperations;
-import org.onosproject.net.flow.FlowRuleOperationsContext;
-import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.intent.FlowObjectiveIntent;
-import org.onosproject.net.intent.FlowRuleIntent;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentData;
-import org.onosproject.net.intent.IntentStore;
-import org.onosproject.net.intent.ProtectionEndpointIntent;
-import org.slf4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static org.onosproject.net.flowobjective.ObjectiveError.INSTALLATIONTHRESHOLDEXCEEDED;
-import static org.onosproject.net.intent.IntentState.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Auxiliary entity responsible for installing the intents into the environment.
- */
-class IntentInstaller {
-
-    private static final Logger log = getLogger(IntentInstaller.class);
-    private static final long OBJECTIVE_RETRY_THRESHOLD = 5;
-
-    private IntentStore store;
-    private ObjectiveTrackerService trackerService;
-    private FlowRuleService flowRuleService;
-    private FlowObjectiveService flowObjectiveService;
-    private NetworkConfigService networkConfigService;
-    private DomainIntentService domainIntentService;
-
-    private enum Direction {
-        ADD,
-        REMOVE
-    }
-
-    /**
-     * Initializes the installer with references to required services.
-     *
-     * @param intentStore          intent store
-     * @param trackerService       objective tracking service
-     * @param flowRuleService      flow rule service
-     * @param flowObjectiveService flow objective service
-     * @param networkConfigService network configuration service
-     * @param domainIntentService  domain intent service
-     */
-    void init(IntentStore intentStore, ObjectiveTrackerService trackerService,
-              FlowRuleService flowRuleService, FlowObjectiveService flowObjectiveService,
-              NetworkConfigService networkConfigService, DomainIntentService domainIntentService) {
-
-        this.store = intentStore;
-        this.trackerService = trackerService;
-        //TODO Various services should be plugged to the intent installer instead of being hardcoded
-        this.flowRuleService = flowRuleService;
-        this.flowObjectiveService = flowObjectiveService;
-        this.networkConfigService = networkConfigService;
-        this.domainIntentService = domainIntentService;
-    }
-
-    // FIXME: Intent Manager should have never become dependent on a specific intent type(s).
-    // This will be addressed in intent domains work; not now.
-
-    /**
-     * Applies the specified intent updates to the environment by uninstalling
-     * and installing the intents and updating the store references appropriately.
-     *
-     * @param toUninstall optional intent to uninstall
-     * @param toInstall   optional intent to install
-     */
-    void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
-        // Hook for handling success at intent installation level.
-        Consumer<IntentInstallationContext> successConsumer = (ctx) -> {
-            if (toInstall.isPresent()) {
-                IntentData installData = toInstall.get();
-                log.debug("Completed installing: {}", installData.key());
-                installData.setState(INSTALLED);
-                store.write(installData);
-            } else if (toUninstall.isPresent()) {
-                IntentData uninstallData = toUninstall.get();
-                log.debug("Completed withdrawing: {}", uninstallData.key());
-                switch (uninstallData.request()) {
-                    case INSTALL_REQ:
-                        // illegal state?
-                        log.warn("{} was requested to withdraw during installation?",
-                                 uninstallData.intent());
-                        uninstallData.setState(FAILED);
-                        break;
-                    case WITHDRAW_REQ:
-                    default: //TODO "default" case should not happen
-                        uninstallData.setState(WITHDRAWN);
-                        break;
-                }
-                // Intent has been withdrawn; we can clear the installables
-                store.write(new IntentData(uninstallData, Collections.emptyList()));
-            }
-        };
-
-        // Hook for handling errors at intent installation level
-        Consumer<IntentInstallationContext> errorConsumer = (ctx) -> {
-            // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
-            if (toInstall.isPresent()) {
-                IntentData installData = toInstall.get();
-                installData.setState(CORRUPT);
-                installData.incrementErrorCount();
-                store.write(installData);
-            }
-            // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
-            if (toUninstall.isPresent()) {
-                IntentData uninstallData = toUninstall.get();
-                uninstallData.setState(CORRUPT);
-                uninstallData.incrementErrorCount();
-                store.write(uninstallData);
-            }
-        };
-
-        // Hooks at operation level
-        Consumer<OperationContext> successOperationConsumer = (ctx) -> {
-            ctx.intentContext.finishContext(ctx);
-        };
-        Consumer<OperationContext> errorOperationConsumer = (ctx) -> {
-            if (ctx.toInstall.isPresent()) {
-                IntentData installData = toInstall.get();
-                log.warn("Failed installation operation for: {} {} due to {}",
-                         installData.key(), installData.intent(), ctx.error());
-            }
-            if (ctx.toUninstall.isPresent()) {
-                IntentData uninstallData = toUninstall.get();
-                log.warn("Failed withdrawal operation for: {} {} due to {}",
-                         uninstallData.key(), uninstallData.intent(), ctx.error());
-            }
-            ctx.intentContext.handleError(ctx);
-        };
-
-        // Create a context for tracking the backing operations for applying
-        // the intents to the environment.
-        IntentInstallationContext intentContext =
-                new IntentInstallationContext(successConsumer, errorConsumer);
-        Set<OperationContext> contexts = createContext(intentContext, toUninstall, toInstall);
-        intentContext.pendingContexts = contexts;
-        contexts.forEach(ctx -> {
-            ctx.prepare(toUninstall, toInstall, successOperationConsumer, errorOperationConsumer);
-            ctx.apply();
-        });
-    }
-
-    // Context for applying and tracking multiple kinds of operation contexts
-    // related to specific intent data.
-    private final class IntentInstallationContext {
-        private Set<OperationContext> pendingContexts = Sets.newHashSet();
-        private Set<OperationContext> errorContexts = Sets.newHashSet();
-        private Consumer<IntentInstallationContext> successConsumer;
-        private Consumer<IntentInstallationContext> errorConsumer;
-
-        private IntentInstallationContext(Consumer<IntentInstallationContext> succesConsumer,
-                                          Consumer<IntentInstallationContext> errorConsumer) {
-            this.successConsumer = succesConsumer;
-            this.errorConsumer = errorConsumer;
-        }
-
-        private void handleError(OperationContext ctx) {
-            errorContexts.add(ctx);
-            finishContext(ctx);
-        }
-
-        private void finishContext(OperationContext ctx) {
-            synchronized (pendingContexts) {
-                pendingContexts.remove(ctx);
-                if (pendingContexts.isEmpty()) {
-                    if (errorContexts.isEmpty()) {
-                        successConsumer.accept(IntentInstallationContext.this);
-                    } else {
-                        errorConsumer.accept(IntentInstallationContext.this);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return MoreObjects.toStringHelper(this)
-                    .add("pendingContexts", pendingContexts)
-                    .add("errorContexts", errorContexts)
-                    .toString();
-        }
-    }
-
-    // --- Utilities to support FlowRule vs. FlowObjective vs. DomainIntent behavior ----
-
-    // Creates the set of contexts appropriate for tracking operations of the
-    // the specified intents.
-    private Set<OperationContext> createContext(IntentInstallationContext intentContext,
-                                                Optional<IntentData> toUninstall,
-                                                Optional<IntentData> toInstall) {
-
-        Set<OperationContext> contexts = Sets.newConcurrentHashSet();
-        if (isInstallable(toUninstall, toInstall, FlowRuleIntent.class)) {
-            contexts.add(new FlowRuleOperationContext(intentContext));
-        }
-        if (isInstallable(toUninstall, toInstall, FlowObjectiveIntent.class)) {
-            contexts.add(new FlowObjectiveOperationContext(intentContext));
-        }
-        if (isInstallable(toUninstall, toInstall, ProtectionEndpointIntent.class)) {
-            contexts.add(new ProtectionConfigOperationContext(intentContext));
-        }
-        if (isInstallable(toUninstall, toInstall, DomainIntent.class)) {
-            contexts.add(new DomainIntentOperationContext(intentContext));
-        }
-
-        if (contexts.isEmpty()) {
-            log.warn("{} did not contain installable Intents", intentContext);
-            return ImmutableSet.of(new ErrorContext(intentContext));
-        }
-
-        return contexts;
-    }
-
-    /**
-     * Tests if one of {@code toUninstall} or {@code toInstall} contains
-     * installable Intent of type specified by {@code intentClass}.
-     *
-     * @param toUninstall IntentData to test
-     * @param toInstall   IntentData to test
-     * @param intentClass installable Intent class
-     * @return true if at least one of IntentData contains installable specified.
-     */
-    private boolean isInstallable(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
-                                  Class<? extends Intent> intentClass) {
-
-        return Stream.concat(toInstall
-                              .map(IntentData::installables)
-                              .map(Collection::stream)
-                              .orElse(Stream.empty()),
-                             toUninstall
-                              .map(IntentData::installables)
-                              .map(Collection::stream)
-                              .orElse(Stream.empty()))
-                .anyMatch(i -> intentClass.isAssignableFrom(i.getClass()));
-    }
-
-    // Base context for applying and tracking operations related to installable intents.
-    private abstract class OperationContext {
-        protected IntentInstallationContext intentContext;
-        protected Optional<IntentData> toUninstall;
-        protected Optional<IntentData> toInstall;
-        /**
-         * Implementation of {@link OperationContext} should call this on success.
-         */
-        protected Consumer<OperationContext> successConsumer;
-        /**
-         * Implementation of {@link OperationContext} should call this on error.
-         */
-        protected Consumer<OperationContext> errorConsumer;
-
-        protected OperationContext(IntentInstallationContext context) {
-            this.intentContext = context;
-        }
-
-        /**
-         * Applies the Intents specified by
-         * {@link #prepareIntents(List, Direction)} call(s) prior to this call.
-         */
-        abstract void apply();
-
-        /**
-         * Returns error state of the context.
-         * <p>
-         * Used for error logging purpose.
-         * Returned Object should have reasonable toString() implementation.
-         * @return context state, describing current error state
-         */
-        abstract Object error();
-
-        /**
-         * Prepares Intent(s) to {@link #apply() apply} in this operation.
-         * <p>
-         * Intents specified by {@code intentsToApply} in a single call
-         * can be applied to the Devices in arbitrary order.
-         * But group of Intents specified in consecutive {@link #prepareIntents(List, Direction)}
-         * calls must be applied in order. (e.g., guarded by barrier)
-         *
-         * @param intentsToApply {@link Intent}s to apply
-         * @param direction of operation
-         */
-        abstract void prepareIntents(List<Intent> intentsToApply, Direction direction);
-
-        void prepare(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
-                     Consumer<OperationContext> successConsumer,
-                     Consumer<OperationContext> errorConsumer) {
-            this.toUninstall = toUninstall;
-            this.toInstall = toInstall;
-            this.successConsumer = successConsumer;
-            this.errorConsumer = errorConsumer;
-            prepareIntentData(toUninstall, toInstall);
-        }
-
-        private void prepareIntentData(Optional<IntentData> uninstallData,
-                                       Optional<IntentData> installData) {
-            if (!installData.isPresent() && !uninstallData.isPresent()) {
-                return;
-            } else if (!installData.isPresent()) {
-                prepareIntentData(uninstallData, Direction.REMOVE);
-            } else if (!uninstallData.isPresent()) {
-                prepareIntentData(installData, Direction.ADD);
-            } else {
-                IntentData uninstall = uninstallData.get();
-                IntentData install = installData.get();
-                List<Intent> uninstallIntents = Lists.newArrayList(uninstall.installables());
-                List<Intent> installIntents = Lists.newArrayList(install.installables());
-
-                checkState(uninstallIntents.stream().allMatch(this::isSupported),
-                           "Unsupported installable intents detected: %s", uninstallIntents);
-                checkState(installIntents.stream().allMatch(this::isSupported),
-                           "Unsupported installable intents detected: %s", installIntents);
-
-                //TODO: Filter FlowObjective intents
-                // Filter out same intents and intents with same flow rules
-                Iterator<Intent> iterator = installIntents.iterator();
-                while (iterator.hasNext()) {
-                    Intent installIntent = iterator.next();
-                    uninstallIntents.stream().filter(uIntent -> {
-                        if (uIntent.equals(installIntent)) {
-                            return true;
-                        } else if (uIntent instanceof FlowRuleIntent && installIntent instanceof FlowRuleIntent) {
-                            //FIXME we can further optimize this by doing the filtering on a flow-by-flow basis
-                            //      (direction can be implied from intent state)
-                            return !flowRuleIntentChanged(((FlowRuleIntent) uIntent),
-                                                          ((FlowRuleIntent) installIntent));
-                        } else {
-                            return false;
-                        }
-                    }).findFirst().ifPresent(common -> {
-                        uninstallIntents.remove(common);
-                        if (INSTALLED.equals(uninstall.state())) {
-                            // only remove the install intent if the existing
-                            // intent (i.e. the uninstall one) is already
-                            // installed or installing
-                            iterator.remove();
-                        }
-                    });
-                }
-
-                final IntentData newUninstall = new IntentData(uninstall, uninstallIntents);
-                final IntentData newInstall = new IntentData(install, installIntents);
-
-                trackerService.removeTrackedResources(newUninstall.key(), newUninstall.intent().resources());
-                uninstallIntents.forEach(installable ->
-                                                 trackerService.removeTrackedResources(newUninstall.intent().key(),
-                                                                                       installable.resources()));
-                trackerService.addTrackedResources(newInstall.key(), newInstall.intent().resources());
-                installIntents.forEach(installable ->
-                                               trackerService.addTrackedResources(newInstall.key(),
-                                                                                  installable.resources()));
-                prepareIntents(uninstallIntents, Direction.REMOVE);
-                prepareIntents(installIntents, Direction.ADD);
-            }
-        }
-
-        /**
-         * Determines whether there is any flow rule changed
-         * (i.e., different set of flow rules or different treatments)
-         * between FlowRuleIntents to be uninstalled and to be installed.
-         *
-         * @param uninstallIntent FlowRuleIntent to uninstall
-         * @param installIntent FlowRuleIntent to install
-         * @return true if flow rules which to be uninstalled
-         * contains all flow rules which to be installed.
-         */
-        private boolean flowRuleIntentChanged(FlowRuleIntent uninstallIntent,
-                                              FlowRuleIntent installIntent) {
-            Collection<FlowRule> flowRulesToUninstall = uninstallIntent.flowRules();
-            Collection<FlowRule> flowRulesToInstall = installIntent.flowRules();
-
-            // Check if any flow rule changed
-            for (FlowRule flowRuleToInstall : flowRulesToInstall) {
-                if (flowRulesToUninstall.stream().noneMatch(flowRuleToInstall::exactMatch)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Applies the specified intent data, if present, to the network using the
-         * specified context.
-         *
-         * @param intentData optional intent data; no-op if not present
-         * @param direction  indicates adding or removal
-         */
-        private void prepareIntentData(Optional<IntentData> intentData, Direction direction) {
-            if (!intentData.isPresent()) {
-                return;
-            }
-
-            IntentData data = intentData.get();
-            List<Intent> intentsToApply = data.installables();
-            checkState(intentsToApply.stream().allMatch(this::isSupported),
-                       "Unsupported installable intents detected: %s", intentsToApply);
-
-            if (direction == Direction.ADD) {
-                trackerService.addTrackedResources(data.key(), data.intent().resources());
-                intentsToApply.forEach(installable ->
-                                               trackerService.addTrackedResources(data.key(),
-                                                                                  installable.resources()));
-            } else {
-                trackerService.removeTrackedResources(data.key(), data.intent().resources());
-                intentsToApply.forEach(installable ->
-                                               trackerService.removeTrackedResources(data.intent().key(),
-                                                                                     installable.resources()));
-            }
-
-            prepareIntents(intentsToApply, direction);
-        }
-
-        private boolean isSupported(Intent intent) {
-            return intent instanceof FlowRuleIntent ||
-                   intent instanceof FlowObjectiveIntent ||
-                   intent instanceof ProtectionEndpointIntent ||
-                   intent instanceof DomainIntent;
-        }
-
-        protected ToStringHelper toStringHelper() {
-            return MoreObjects.toStringHelper(this)
-                    .add("intentContext", intentContext)
-                    .add("toUninstall", toUninstall)
-                    .add("toInstall", toInstall);
-        }
-
-        @Override
-        public String toString() {
-            return toStringHelper()
-                    .toString();
-        }
-    }
-
-
-    // Context for applying and tracking operations related to flow rule intents.
-    private class FlowRuleOperationContext extends OperationContext {
-        FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
-        FlowRuleOperationsContext flowRuleOperationsContext;
-
-        FlowRuleOperationContext(IntentInstallationContext context) {
-            super(context);
-        }
-
-        @Override
-        void apply() {
-            flowRuleOperationsContext = new FlowRuleOperationsContext() {
-                @Override
-                public void onSuccess(FlowRuleOperations ops) {
-                    successConsumer.accept(FlowRuleOperationContext.this);
-                }
-
-                @Override
-                public void onError(FlowRuleOperations ops) {
-                    errorConsumer.accept(FlowRuleOperationContext.this);
-                }
-            };
-            FlowRuleOperations operations = builder.build(flowRuleOperationsContext);
-
-            if (log.isTraceEnabled()) {
-                log.trace("applying intent {} -> {} with {} rules: {}",
-                          toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
-                          toInstall.map(x -> x.key().toString()).orElse("<empty>"),
-                          operations.stages().stream().mapToLong(Set::size).sum(),
-                          operations.stages());
-            }
-
-            flowRuleService.apply(operations);
-        }
-
-        @Override
-        public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
-            // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so.
-            builder.newStage();
-
-            List<Collection<FlowRule>> stages = intentsToApply.stream()
-                    .filter(x -> x instanceof FlowRuleIntent)
-                    .map(x -> (FlowRuleIntent) x)
-                    .map(FlowRuleIntent::flowRules)
-                    .collect(Collectors.toList());
-
-            for (Collection<FlowRule> rules : stages) {
-                if (direction == Direction.ADD) {
-                    rules.forEach(builder::add);
-                } else {
-                    rules.forEach(builder::remove);
-                }
-            }
-
-        }
-
-        @Override
-        public Object error() {
-            return flowRuleOperationsContext;
-        }
-
-        @Override
-        protected ToStringHelper toStringHelper() {
-            return super.toStringHelper()
-                    .omitNullValues()
-                    .add("flowRuleOperationsContext", flowRuleOperationsContext);
-        }
-    }
-
-    // Context for applying and tracking operations related to flow objective intents.
-    private class FlowObjectiveOperationContext extends OperationContext {
-        private static final String UNSUPPORT_OBJ = "unsupported objective {}";
-        final List<ObjectiveContext> contexts = Lists.newArrayList();
-
-        final Set<ObjectiveContext> pendingContexts = Sets.newConcurrentHashSet();
-
-        // Second stage of pending contexts
-        final Set<ObjectiveContext> nextPendingContexts = Sets.newConcurrentHashSet();
-        final Set<ObjectiveContext> errorContexts = Sets.newConcurrentHashSet();
-
-        FlowObjectiveOperationContext(IntentInstallationContext context) {
-            super(context);
-        }
-
-        @Override
-        public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
-            intentsToApply
-                .stream()
-                .filter(intent -> intent instanceof FlowObjectiveIntent)
-                .map(intent -> buildObjectiveContexts((FlowObjectiveIntent) intent, direction))
-                .flatMap(Collection::stream)
-                .forEach(contexts::add);
-
-            // Two stage for different direction context
-            // We will apply REMOVE context first, and apply ADD context.
-            contexts.forEach(context -> {
-                switch (direction) {
-                    case REMOVE:
-                        pendingContexts.add(context);
-                        break;
-                    case ADD:
-                        nextPendingContexts.add(context);
-                        break;
-                    default:
-                        break;
-                }
-            });
-        }
-
-        // Builds the specified objective in the appropriate direction
-        private Set<? extends ObjectiveContext> buildObjectiveContexts(FlowObjectiveIntent intent,
-                                            Direction direction) {
-            Set<FlowObjectiveInstallationContext> contexts = Sets.newHashSet();
-            int size = intent.objectives().size();
-            List<Objective> objectives = intent.objectives();
-            List<DeviceId> deviceIds = intent.devices();
-
-            if (direction == Direction.ADD) {
-                for (int i = 0; i < size; i++) {
-                    Objective objective = objectives.get(i);
-                    DeviceId deviceId = deviceIds.get(i);
-                    FlowObjectiveInstallationContext ctx =
-                            buildObjectiveContext(objective, deviceId, direction);
-                    contexts.add(ctx);
-                }
-                return contexts;
-            } else {
-                // we need to care about ordering here
-                // basic idea is to chain objective contexts
-                for (int i = 0; i < size; i++) {
-                    Objective objective = intent.objectives().get(i);
-                    DeviceId deviceId = intent.devices().get(i);
-
-                    if (objective instanceof FilteringObjective) {
-                        // don't need to care ordering of filtering objective
-                        FlowObjectiveInstallationContext ctx =
-                                buildObjectiveContext(objective, deviceId, direction);
-                        contexts.add(ctx);
-                    } else if (objective instanceof NextObjective) {
-                        // need to removed after forwarding objective
-                        // nothing to do here
-                    } else if (objective instanceof ForwardingObjective) {
-                        // forwarding objective, also find next objective if
-                        // exist
-                        FlowObjectiveInstallationContext fwdCtx =
-                                buildObjectiveContext(objective, deviceId, direction);
-                        ForwardingObjective fwd = (ForwardingObjective) objective;
-                        NextObjective nxt = null;
-                        Integer nextId = fwd.nextId();
-                        if (nextId != null) {
-                            for (int j = 0; j < size; j++) {
-                                if (objectives.get(j).id() == nextId) {
-                                    nxt = (NextObjective) objectives.get(j);
-                                    break;
-                                }
-                            }
-                            // if a next objective exists in the Intent
-                            if (nxt != null) {
-                                FlowObjectiveInstallationContext nxtCtx =
-                                        buildObjectiveContext(nxt, deviceId, direction);
-                                fwdCtx.nextContext(nxtCtx);
-                            }
-                        }
-                        contexts.add(fwdCtx);
-                    } else {
-                        // possible here?
-                        log.warn(UNSUPPORT_OBJ, objective);
-                    }
-                }
-            }
-            return contexts;
-        }
-
-        private FlowObjectiveInstallationContext buildObjectiveContext(Objective objective,
-                                                                       DeviceId deviceId,
-                                                                       Direction direction) {
-            Objective.Builder builder = objective.copy();
-            FlowObjectiveInstallationContext ctx = new FlowObjectiveInstallationContext();
-            switch (direction) {
-                case ADD:
-                    objective = builder.add(ctx);
-                    break;
-                case REMOVE:
-                    objective = builder.remove(ctx);
-                    break;
-                default:
-                    break;
-            }
-            ctx.setObjective(objective, deviceId);
-            return ctx;
-        }
-
-        @Override
-        void apply() {
-            // If there is no pending contexts, try apply second stage
-            // pending contexts
-            if (pendingContexts.isEmpty()) {
-                pendingContexts.addAll(nextPendingContexts);
-                nextPendingContexts.clear();
-            }
-            final Set<ObjectiveContext> contextsToApply = Sets.newHashSet(pendingContexts);
-            contextsToApply.forEach(ctx -> {
-                FlowObjectiveInstallationContext foiCtx =
-                        (FlowObjectiveInstallationContext) ctx;
-
-                flowObjectiveService.apply(foiCtx.deviceId, foiCtx.objective);
-            });
-        }
-
-        @Override
-        public Object error() {
-            return errorContexts;
-        }
-
-        @Override
-        protected ToStringHelper toStringHelper() {
-            return super.toStringHelper()
-                    .add("contexts", contexts)
-                    .add("pendingContexts", pendingContexts)
-                    .add("errorContexts", errorContexts);
-        }
-
-        private class FlowObjectiveInstallationContext implements ObjectiveContext {
-            Objective objective;
-            DeviceId deviceId;
-            ObjectiveError error;
-            AtomicInteger retry;
-            FlowObjectiveInstallationContext nextContext;
-
-            void setObjective(Objective objective, DeviceId deviceId) {
-                // init function
-                this.objective = objective;
-                this.deviceId = deviceId;
-                this.error = null;
-                this.retry = new AtomicInteger(0);
-                this.nextContext = null;
-            }
-
-            int retryTimes() {
-                return this.retry.get();
-            }
-
-            void increaseRetryValue() {
-                this.retry.incrementAndGet();
-            }
-
-            private void finished(ObjectiveError error) {
-
-                synchronized (pendingContexts) {
-                    if (error != null) {
-                        this.error = error;
-                        handleObjectiveError(this, error);
-                    } else {
-                        // apply next context if exist
-                        if (nextContext != null) {
-                            pendingContexts.add(nextContext);
-                            flowObjectiveService.apply(nextContext.deviceId,
-                                                       nextContext.objective);
-                            pendingContexts.remove(this);
-                        } else {
-                            pendingContexts.remove(this);
-                        }
-                    }
-                    if (!pendingContexts.isEmpty()) {
-                        return;
-                    }
-                    // Apply second stage pending contexts if it is not empty
-                    if (!nextPendingContexts.isEmpty()) {
-                        pendingContexts.addAll(nextPendingContexts);
-                        nextPendingContexts.clear();
-                        final Set<ObjectiveContext> contextsToApply =
-                                Sets.newHashSet(pendingContexts);
-                        contextsToApply.forEach(ctx -> {
-                            FlowObjectiveInstallationContext foiCtx =
-                                    (FlowObjectiveInstallationContext) ctx;
-                            flowObjectiveService.apply(foiCtx.deviceId,
-                                                       foiCtx.objective);
-                        });
-                        return;
-                    }
-                    if (errorContexts.isEmpty()) {
-                        successConsumer.accept(FlowObjectiveOperationContext.this);
-                    } else {
-                        errorConsumer.accept(FlowObjectiveOperationContext.this);
-                    }
-                }
-            }
-
-            @Override
-            public void onSuccess(Objective objective) {
-                finished(null);
-            }
-
-            @Override
-            public void onError(Objective objective, ObjectiveError error) {
-                finished(error);
-            }
-
-            @Override
-            public String toString() {
-                return String.format("(%s on %s for %s)", error, deviceId, objective);
-            }
-
-            public void nextContext(FlowObjectiveInstallationContext nextContext) {
-                this.nextContext = nextContext;
-            }
-        }
-
-        private void handleObjectiveError(FlowObjectiveInstallationContext ctx,
-                                          ObjectiveError error) {
-            log.debug("Got error(s) when install objective: {}, error: {}, retry: {}",
-                      ctx.objective, ctx.error, ctx.retry);
-            if (ctx.retryTimes() > OBJECTIVE_RETRY_THRESHOLD) {
-                ctx.error = INSTALLATIONTHRESHOLDEXCEEDED;
-                errorContexts.add(ctx);
-                return;
-            }
-            // reset error
-            ctx.error = null;
-            // strategies for errors
-            switch (error) {
-                case GROUPEXISTS:
-                    if (ctx.objective.op() == Objective.Operation.ADD) {
-                        // Next group exists
-                        // build new objective with new op ADD_TO_EXIST
-                        NextObjective newObj =
-                                ((NextObjective.Builder) ctx.objective.copy()).addToExisting(ctx);
-                        ctx.setObjective(newObj, ctx.deviceId);
-                        ctx.increaseRetryValue();
-                        flowObjectiveService.apply(ctx.deviceId, ctx.objective);
-                    } else {
-                        pendingContexts.remove(ctx);
-                        errorContexts.add(ctx);
-                    }
-                    break;
-                case GROUPINSTALLATIONFAILED:
-                    // Group install failed, retry again
-                    ctx.increaseRetryValue();
-                    flowObjectiveService.apply(ctx.deviceId, ctx.objective);
-                    break;
-                case GROUPMISSING:
-                    if (ctx.objective.op() == Objective.Operation.ADD_TO_EXISTING) {
-                        // Next group not exist, but we want to add new buckets
-                        // build new objective with new op ADD
-                        NextObjective newObj = (NextObjective) ctx.objective.copy().add(ctx);
-                        ctx.setObjective(newObj, ctx.deviceId);
-                        ctx.increaseRetryValue();
-                        flowObjectiveService.apply(ctx.deviceId, ctx.objective);
-                    } else if (ctx.objective.op() == Objective.Operation.REMOVE ||
-                            ctx.objective.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
-                        // Already removed, no need to do anything
-                        ctx.error = null;
-                        pendingContexts.remove(ctx);
-                        return;
-                    } else {
-                        // Next chaining group missing, try again.
-                        ctx.increaseRetryValue();
-                        flowObjectiveService.apply(ctx.deviceId, ctx.objective);
-                    }
-                    break;
-                case FLOWINSTALLATIONFAILED:
-                case GROUPREMOVALFAILED:
-                case INSTALLATIONTIMEOUT:
-                    // Retry
-                    ctx.increaseRetryValue();
-                    flowObjectiveService.apply(ctx.deviceId, ctx.objective);
-                    break;
-                default:
-                    pendingContexts.remove(ctx);
-                    errorContexts.add(ctx);
-                    break;
-            }
-        }
-    }
-
-    // Context for applying and tracking operations related to domain intents.
-    private class DomainIntentOperationContext extends OperationContext {
-        DomainIntentOperations.Builder builder = DomainIntentOperations.builder();
-        DomainIntentOperationsContext domainOperationsContext;
-
-        DomainIntentOperationContext(IntentInstallationContext context) {
-            super(context);
-        }
-        @Override
-        void apply() {
-            domainOperationsContext = new DomainIntentOperationsContext() {
-                @Override
-                public void onSuccess(DomainIntentOperations ops) {
-                    successConsumer.accept(DomainIntentOperationContext.this);
-                }
-
-                @Override
-                public void onError(DomainIntentOperations ops) {
-                    errorConsumer.accept(DomainIntentOperationContext.this);
-                }
-            };
-            DomainIntentOperations operations = builder.build(domainOperationsContext);
-
-            if (log.isTraceEnabled()) {
-                log.trace("submitting domain intent {} -> {}",
-                          toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
-                          toInstall.map(x -> x.key().toString()).orElse("<empty>"));
-            }
-            domainIntentService.sumbit(operations);
-        }
-
-        @Override
-        public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
-            List<DomainIntent> intents = intentsToApply.stream()
-                    .filter(x -> x instanceof DomainIntent)
-                    .map(x -> (DomainIntent) x)
-                    .collect(Collectors.toList());
-
-            for (DomainIntent intent : intents) {
-                if (direction == Direction.ADD) {
-                    builder.add(intent);
-                } else {
-                    builder.remove(intent);
-                }
-            }
-        }
-
-        @Override
-        public Object error() {
-            return domainOperationsContext;
-        }
-    }
-
-    private class ErrorContext extends OperationContext {
-        ErrorContext(IntentInstallationContext context) {
-            super(context);
-        }
-        @Override
-        void apply() {
-            throw new UnsupportedOperationException("Unsupported installable intent");
-        }
-
-        @Override
-        Object error() {
-            return null;
-        }
-
-        @Override
-        void prepareIntents(List<Intent> intentsToApply, Direction direction) {
-        }
-    }
-
-
-    /**
-     * Context for applying and tracking operations related to
-     * {@link ProtectionEndpointIntent}.
-     */
-    @Beta
-    private class ProtectionConfigOperationContext extends OperationContext {
-
-        ProtectionConfigOperationContext(IntentInstallationContext context) {
-            super(context);
-        }
-
-        /**
-         * Stage of installable Intents which can be processed in parallel.
-         */
-        private final class Stage {
-            // should it have progress state, how far it went?
-            private final Collection<Pair<ProtectionEndpointIntent, Direction>> ops;
-
-            Stage(Collection<Pair<ProtectionEndpointIntent, Direction>> ops) {
-                this.ops = checkNotNull(ops);
-            }
-
-            CompletableFuture<Void> apply() {
-                return ops.stream()
-                    .map(op -> applyOp(op.getRight(), op.getLeft()))
-                    .reduce(CompletableFuture.completedFuture(null),
-                            (l, r) -> {
-                                l.join();
-                                return r;
-                            });
-            }
-
-            private CompletableFuture<Void> applyOp(Direction dir, ProtectionEndpointIntent intent) {
-                log.trace("applying {}: {}", dir, intent);
-                if (dir == Direction.REMOVE) {
-                    networkConfigService.removeConfig(intent.deviceId(), ProtectionConfig.class);
-                } else if (dir == Direction.ADD) {
-                    ProtectedTransportEndpointDescription description = intent.description();
-
-                    // Can't do following. Will trigger empty CONFIG_ADDED
-                    //ProtectionConfig cfg = networkConfigService.addConfig(intent.deviceId(),
-                    //                                                      ProtectionConfig.class);
-                    ProtectionConfig cfg = new ProtectionConfig(intent.deviceId());
-                    cfg.fingerprint(description.fingerprint());
-                    cfg.peer(description.peer());
-                    cfg.paths(description.paths());
-                    //cfg.apply();
-
-                    networkConfigService.applyConfig(intent.deviceId(),
-                                                     ProtectionConfig.class,
-                                                     cfg.node());
-                }
-                // TODO Should monitor progress and complete only after it's
-                // actually done.
-                return CompletableFuture.completedFuture(null);
-            }
-
-            @Override
-            public String toString() {
-                return ops.toString();
-            }
-        }
-
-        /**
-         * List of Stages which must be executed in order.
-         */
-        private final List<Stage> stages = new ArrayList<>();
-
-        private final List<Stage> failed = new CopyOnWriteArrayList<>();
-
-        @Override
-        synchronized void apply() {
-            for (Stage stage : stages) {
-                log.trace("applying Stage {}", stage);
-                CompletableFuture<Void> result = stage.apply();
-                // wait for stage completion
-                result.join();
-                if (result.isCompletedExceptionally()) {
-                    log.error("Stage {} failed", stage);
-                    failed.add(stage);
-                    errorConsumer.accept(ProtectionConfigOperationContext.this);
-                    return;
-                }
-            }
-            successConsumer.accept(ProtectionConfigOperationContext.this);
-        }
-
-        @Override
-        Object error() {
-            // Something to represent error state
-            return failed;
-        }
-
-        @Override
-        synchronized void prepareIntents(List<Intent> intentsToApply,
-                                         Direction direction) {
-
-            stages.add(new Stage(intentsToApply.stream()
-                                 .filter(i -> i instanceof ProtectionEndpointIntent)
-                                 .map(i -> Pair.of((ProtectionEndpointIntent) i, direction))
-                                 .collect(Collectors.toList())));
-        }
-
-        @Override
-        protected ToStringHelper toStringHelper() {
-            return super.toStringHelper()
-                    .add("stages", stages)
-                    .add("failed", failed);
-        }
-    }
-}
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 3b542bd..14d2bea 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
@@ -38,9 +38,12 @@
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentBatchDelegate;
 import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentInstallCoordinator;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentEvent;
 import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentInstaller;
 import org.onosproject.net.intent.IntentListener;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
@@ -87,7 +90,7 @@
 @Service
 public class IntentManager
         extends AbstractListenerManager<IntentEvent, IntentListener>
-        implements IntentService, IntentExtensionService {
+        implements IntentService, IntentExtensionService, IntentInstallCoordinator {
 
     private static final Logger log = getLogger(IntentManager.class);
 
@@ -141,17 +144,17 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private NetworkConfigService networkConfigService;
 
-
     private ExecutorService batchExecutor;
     private ExecutorService workerExecutor;
 
-    private final IntentInstaller intentInstaller = new IntentInstaller();
     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 IntentStoreDelegate testOnlyDelegate = new TestOnlyIntentStoreDelegate();
     private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
     private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
+    private InstallCoordinator installCoordinator;
     private IdGenerator idGenerator;
 
     private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
@@ -159,9 +162,6 @@
     @Activate
     public void activate() {
         configService.registerProperties(getClass());
-
-        intentInstaller.init(store, trackerService, flowRuleService, flowObjectiveService,
-                             networkConfigService, domainIntentService);
         if (skipReleaseResourcesOnWithdrawal) {
             store.setDelegate(testOnlyDelegate);
         } else {
@@ -174,12 +174,12 @@
         idGenerator = coreService.getIdGenerator("intent-ids");
         Intent.unbindIdGenerator(idGenerator);
         Intent.bindIdGenerator(idGenerator);
+        installCoordinator = new InstallCoordinator(installerRegistry, store);
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
-        intentInstaller.init(null, null, null, null, null, null);
         if (skipReleaseResourcesOnWithdrawal) {
             store.unsetDelegate(testOnlyDelegate);
         } else {
@@ -337,11 +337,42 @@
     }
 
     @Override
+    public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+        installerRegistry.registerInstaller(cls, installer);
+    }
+
+    @Override
+    public <T extends Intent> void unregisterInstaller(Class<T> cls) {
+        installerRegistry.unregisterInstaller(cls);
+    }
+
+    @Override
+    public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
+        return installerRegistry.getInstallers();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends Intent> IntentInstaller<T> getInstaller(Class<T> cls) {
+        return (IntentInstaller<T>) installerRegistry.getInstallers().get(cls);
+    }
+
+    @Override
     public Iterable<Intent> getPending() {
         checkPermission(INTENT_READ);
         return store.getPending();
     }
 
+    @Override
+    public void intentInstallSuccess(IntentOperationContext context) {
+        installCoordinator.success(context);
+    }
+
+    @Override
+    public void intentInstallFailed(IntentOperationContext context) {
+        installCoordinator.failed(context);
+    }
+
     // Store delegate to re-post events emitted from the store.
     private class InternalStoreDelegate implements IntentStoreDelegate {
         @Override
@@ -536,7 +567,7 @@
 
         @Override
         public void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
-            intentInstaller.apply(toUninstall, toInstall);
+            installCoordinator.installIntents(toUninstall, toInstall);
         }
     }
 
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/installer/DomainIntentInstaller.java b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/DomainIntentInstaller.java
new file mode 100644
index 0000000..a713034
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/DomainIntentInstaller.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.domain.DomainIntent;
+import org.onosproject.net.domain.DomainIntentOperations;
+import org.onosproject.net.domain.DomainIntentOperationsContext;
+import org.onosproject.net.domain.DomainIntentService;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.ObjectiveTrackerService;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Installer for domain Intent.
+ */
+@Component(immediate = true)
+public class DomainIntentInstaller implements IntentInstaller<DomainIntent> {
+
+    private final Logger log = getLogger(IntentManager.class);
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentExtensionService intentExtensionService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ObjectiveTrackerService trackerService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentInstallCoordinator intentInstallCoordinator;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DomainIntentService domainIntentService;
+
+    @Activate
+    public void activated() {
+        intentExtensionService.registerInstaller(DomainIntent.class, this);
+    }
+
+    @Deactivate
+    public void deactivated() {
+        intentExtensionService.unregisterInstaller(DomainIntent.class);
+    }
+
+    @Override
+    public void apply(IntentOperationContext<DomainIntent> context) {
+        Optional<IntentData> toUninstall = context.toUninstall();
+        Optional<IntentData> toInstall = context.toInstall();
+
+        List<DomainIntent> uninstallIntents = context.intentsToUninstall();
+        List<DomainIntent> installIntents = context.intentsToInstall();
+
+        if (!toInstall.isPresent() && !toUninstall.isPresent()) {
+            intentInstallCoordinator.intentInstallSuccess(context);
+            return;
+        }
+
+        if (toUninstall.isPresent()) {
+            IntentData intentData = toUninstall.get();
+            trackerService.removeTrackedResources(intentData.key(), intentData.intent().resources());
+            uninstallIntents.forEach(installable ->
+                                             trackerService.removeTrackedResources(intentData.intent().key(),
+                                                                                   installable.resources()));
+        }
+
+        if (toInstall.isPresent()) {
+            IntentData intentData = toInstall.get();
+            trackerService.addTrackedResources(intentData.key(), intentData.intent().resources());
+            installIntents.forEach(installable ->
+                                           trackerService.addTrackedResources(intentData.key(),
+                                                                              installable.resources()));
+        }
+
+        // Generate domain Intent operations
+        DomainIntentOperations.Builder builder = DomainIntentOperations.builder();
+        DomainIntentOperationsContext domainOperationsContext;
+
+        uninstallIntents.forEach(builder::remove);
+        installIntents.forEach(builder::add);
+
+        domainOperationsContext = new DomainIntentOperationsContext() {
+            @Override
+            public void onSuccess(DomainIntentOperations idops) {
+                intentInstallCoordinator.intentInstallSuccess(context);
+            }
+
+            @Override
+            public void onError(DomainIntentOperations idos) {
+                intentInstallCoordinator.intentInstallFailed(context);
+            }
+        };
+        log.debug("submitting domain intent {} -> {}",
+                  toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
+                  toInstall.map(x -> x.key().toString()).orElse("<empty>"));
+
+        // Submit domain Inten operations with domain context
+        domainIntentService.sumbit(builder.build(domainOperationsContext));
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstaller.java b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstaller.java
new file mode 100644
index 0000000..22ff970
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstaller.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.intent.FlowObjectiveIntent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.ObjectiveTrackerService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.onosproject.net.flowobjective.ObjectiveError.INSTALLATIONTHRESHOLDEXCEEDED;
+import static org.onosproject.net.intent.IntentInstaller.Direction.ADD;
+import static org.onosproject.net.intent.IntentInstaller.Direction.REMOVE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Installer for FlowObjectiveIntent.
+ */
+@Component(immediate = true)
+public class FlowObjectiveIntentInstaller implements IntentInstaller<FlowObjectiveIntent> {
+    private static final int OBJECTIVE_RETRY_THRESHOLD = 5;
+    private static final String UNSUPPORT_OBJ = "unsupported objective {}";
+    private final Logger log = getLogger(IntentManager.class);
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentExtensionService intentExtensionService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ObjectiveTrackerService trackerService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentInstallCoordinator intentInstallCoordinator;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
+    @Activate
+    public void activate() {
+        intentExtensionService.registerInstaller(FlowObjectiveIntent.class, this);
+    }
+
+    @Deactivate
+    public void deactivated() {
+        intentExtensionService.unregisterInstaller(FlowObjectiveIntent.class);
+    }
+
+    @Override
+    public void apply(IntentOperationContext<FlowObjectiveIntent> intentOperationContext) {
+        Objects.requireNonNull(intentOperationContext);
+        Optional<IntentData> toUninstall = intentOperationContext.toUninstall();
+        Optional<IntentData> toInstall = intentOperationContext.toInstall();
+
+        List<FlowObjectiveIntent> uninstallIntents = intentOperationContext.intentsToUninstall();
+        List<FlowObjectiveIntent> installIntents = intentOperationContext.intentsToInstall();
+
+        if (!toInstall.isPresent() && !toUninstall.isPresent()) {
+            intentInstallCoordinator.intentInstallSuccess(intentOperationContext);
+            return;
+        }
+
+        if (toUninstall.isPresent()) {
+            IntentData intentData = toUninstall.get();
+            trackerService.removeTrackedResources(intentData.key(), intentData.intent().resources());
+            uninstallIntents.forEach(installable ->
+                                             trackerService.removeTrackedResources(intentData.intent().key(),
+                                                                                   installable.resources()));
+        }
+
+        if (toInstall.isPresent()) {
+            IntentData intentData = toInstall.get();
+            trackerService.addTrackedResources(intentData.key(), intentData.intent().resources());
+            installIntents.forEach(installable ->
+                                           trackerService.addTrackedResources(intentData.key(),
+                                                                              installable.resources()));
+        }
+
+        FlowObjectiveIntentInstallationContext intentInstallationContext =
+                new FlowObjectiveIntentInstallationContext(intentOperationContext);
+
+        uninstallIntents.stream()
+                .map(intent -> buildObjectiveContexts(intent, REMOVE))
+                .flatMap(Collection::stream)
+                .forEach(context -> {
+                    context.intentInstallationContext(intentInstallationContext);
+                    intentInstallationContext.addContext(context);
+                    intentInstallationContext.addPendingContext(context);
+                });
+
+        installIntents.stream()
+                .map(intent -> buildObjectiveContexts(intent, ADD))
+                .flatMap(Collection::stream)
+                .forEach(context -> {
+                    context.intentInstallationContext(intentInstallationContext);
+                    intentInstallationContext.addContext(context);
+                    intentInstallationContext.addNextPendingContext(context);
+                });
+
+        intentInstallationContext.apply();
+    }
+
+    /**
+     * Builds all objective contexts for a given flow objective Intent with given
+     * operation.
+     *
+     * @param intent the flow objective Intent
+     * @param direction the operation of this Intent
+     * @return all objective context of the Intent with given operation
+     */
+    private Set<FlowObjectiveInstallationContext> buildObjectiveContexts(FlowObjectiveIntent intent,
+                                                                         Direction direction) {
+        Objects.requireNonNull(intent);
+        Objects.requireNonNull(direction);
+        Set<FlowObjectiveInstallationContext> contexts = Sets.newHashSet();
+        int size = intent.objectives().size();
+        List<Objective> objectives = intent.objectives();
+        List<DeviceId> deviceIds = intent.devices();
+
+        if (direction == ADD) {
+            // Install objectives
+            // The flow objective system will handle the installation order
+            for (int i = 0; i < size; i++) {
+                Objective objective = objectives.get(i);
+                DeviceId deviceId = deviceIds.get(i);
+                FlowObjectiveInstallationContext ctx = buildObjectiveContext(objective, deviceId, direction);
+                contexts.add(ctx);
+            }
+            return contexts;
+        } else {
+            // Uninstall objecitves
+            // we need to care about ordering here
+            // basic idea is to chain objective contexts
+            for (int i = 0; i < size; i++) {
+                Objective objective = intent.objectives().get(i);
+                DeviceId deviceId = intent.devices().get(i);
+                if (objective instanceof FilteringObjective) {
+                    // don't need to care ordering of filtering objective
+                    FlowObjectiveInstallationContext ctx =
+                            buildObjectiveContext(objective, deviceId, direction);
+                    contexts.add(ctx);
+                } else if (objective instanceof NextObjective) {
+                    // need to removed after forwarding objective
+                    // nothing to do here
+                } else if (objective instanceof ForwardingObjective) {
+                    // forwarding objective, also find next objective if
+                    // exist
+                    FlowObjectiveInstallationContext fwdCtx =
+                            buildObjectiveContext(objective, deviceId, direction);
+                    ForwardingObjective fwd = (ForwardingObjective) objective;
+                    NextObjective nxt = null;
+                    Integer nextId = fwd.nextId();
+                    if (nextId != null) {
+                        for (int j = 0; j < size; j++) {
+                            if (objectives.get(j).id() == nextId) {
+                                nxt = (NextObjective) objectives.get(j);
+                                break;
+                            }
+                        }
+                        // if a next objective exists in the Intent
+                        if (nxt != null) {
+                            FlowObjectiveInstallationContext nxtCtx =
+                                    buildObjectiveContext(nxt, deviceId, direction);
+                            fwdCtx.nextContext(nxtCtx);
+                        }
+                    }
+                    contexts.add(fwdCtx);
+                } else {
+                    // possible here?
+                    log.warn(UNSUPPORT_OBJ, objective);
+                }
+            }
+        }
+        return contexts;
+    }
+
+    private FlowObjectiveInstallationContext buildObjectiveContext(Objective objective,
+                                                                   DeviceId deviceId,
+                                                                   Direction direction) {
+        Objects.requireNonNull(objective);
+        Objects.requireNonNull(deviceId);
+        Objects.requireNonNull(direction);
+        Objective.Builder builder = objective.copy();
+        FlowObjectiveInstallationContext ctx = new FlowObjectiveInstallationContext();
+        switch (direction) {
+            case ADD:
+                objective = builder.add(ctx);
+                break;
+            case REMOVE:
+                objective = builder.remove(ctx);
+                break;
+            default:
+                break;
+        }
+        ctx.setObjective(objective, deviceId);
+        return ctx;
+    }
+
+    /**
+     * Installation context for flow objective.
+     * Manages installation state of a flow objective.
+     */
+    class FlowObjectiveInstallationContext implements ObjectiveContext {
+        private Objective objective;
+        private DeviceId deviceId;
+        private ObjectiveError error;
+        private AtomicInteger retry;
+        private FlowObjectiveInstallationContext nextContext;
+        private FlowObjectiveIntentInstallationContext intentInstallationContext;
+
+        /**
+         * Set flow objective Intent installation context to this context.
+         *
+         * @param intentInstallationContext the Intent installation context
+         */
+        public void intentInstallationContext(FlowObjectiveIntentInstallationContext intentInstallationContext) {
+            Objects.requireNonNull(intentInstallationContext);
+            this.intentInstallationContext = intentInstallationContext;
+
+            // Set Intent installation context to the next context if exists.
+            if (nextContext != null) {
+                nextContext.intentInstallationContext(intentInstallationContext);
+            }
+        }
+
+        /**
+         * Sets next flow objective installation context.
+         *
+         * @param nextContext the next flow objective installation context
+         */
+        public void nextContext(FlowObjectiveInstallationContext nextContext) {
+            Objects.requireNonNull(nextContext);
+            this.nextContext = nextContext;
+        }
+
+        /**
+         * Sets objective and device id to this context; reset error states.
+         *
+         * @param objective the objective
+         * @param deviceId the device id
+         */
+        void setObjective(Objective objective, DeviceId deviceId) {
+            Objects.requireNonNull(objective);
+            Objects.requireNonNull(deviceId);
+            this.objective = objective;
+            this.deviceId = deviceId;
+            this.error = null;
+            this.retry = new AtomicInteger(0);
+        }
+
+        /**
+         * Gets the number of retries.
+         *
+         * @return the retry count
+         */
+        int retryTimes() {
+            return this.retry.get();
+        }
+
+        /**
+         * Increases the number of retries.
+         */
+        void increaseRetryValue() {
+            this.retry.incrementAndGet();
+        }
+
+        /**
+         * Completed this context.
+         *
+         * @param error the error of this context if exist; null otherwise
+         */
+        private void finished(ObjectiveError error) {
+            synchronized (intentInstallationContext) {
+                if (error != null) {
+                    this.error = error;
+                    intentInstallationContext.handleObjectiveError(this, error);
+                } else {
+                    // apply next context if exist
+                    if (nextContext != null) {
+                        intentInstallationContext.addPendingContext(nextContext);
+                        flowObjectiveService.apply(nextContext.deviceId,
+                                                   nextContext.objective);
+                        intentInstallationContext.removePendingContext(this);
+                    } else {
+                        intentInstallationContext.removePendingContext(this);
+                    }
+                }
+                if (!intentInstallationContext.pendingContexts().isEmpty()) {
+                    return;
+                }
+                // Apply second stage pending contexts if it is not empty
+                if (!intentInstallationContext.nextPendingContexts().isEmpty()) {
+                    intentInstallationContext.moveNextPendingToPending();
+                    final Set<ObjectiveContext> contextsToApply =
+                            Sets.newHashSet(intentInstallationContext.pendingContexts());
+                    contextsToApply.forEach(ctx -> {
+                        FlowObjectiveInstallationContext foiCtx = (FlowObjectiveInstallationContext) ctx;
+                        flowObjectiveService.apply(foiCtx.deviceId,
+                                                   foiCtx.objective);
+                    });
+                    return;
+                }
+                if (intentInstallationContext.errorContexts().isEmpty()) {
+                    intentInstallCoordinator.intentInstallSuccess(intentInstallationContext.intentOperationContext());
+                } else {
+                    intentInstallCoordinator.intentInstallFailed(intentInstallationContext.intentOperationContext());
+                }
+            }
+        }
+
+        @Override
+        public void onSuccess(Objective objective) {
+            finished(null);
+        }
+
+        @Override
+        public void onError(Objective objective, ObjectiveError error) {
+            finished(error);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("(%s on %s for %s)", error, deviceId, objective);
+        }
+    }
+
+    /**
+     * Installation context for FlowObjective Intent.
+     * Manages states of pending and error flow objective contexts.
+     */
+    class FlowObjectiveIntentInstallationContext {
+        private final IntentOperationContext<FlowObjectiveIntent> intentOperationContext;
+        final List<ObjectiveContext> contexts = Lists.newArrayList();
+        final Set<ObjectiveContext> errorContexts = Sets.newConcurrentHashSet();
+        final Set<ObjectiveContext> pendingContexts = Sets.newConcurrentHashSet();
+
+        // Second stage of pending contexts
+        final Set<ObjectiveContext> nextPendingContexts = Sets.newConcurrentHashSet();
+
+        /**
+         * Creates a flow objective installation context.
+         *
+         * @param intentOperationContext the flow objective installation context
+         */
+        public FlowObjectiveIntentInstallationContext(
+                IntentOperationContext<FlowObjectiveIntent> intentOperationContext) {
+            Objects.requireNonNull(intentOperationContext);
+            this.intentOperationContext = intentOperationContext;
+        }
+
+        /**
+         * Gets Intent operation context of this context.
+         *
+         * @return the Intent operation context
+         */
+        public IntentOperationContext<FlowObjectiveIntent> intentOperationContext() {
+            return intentOperationContext;
+        }
+
+        /**
+         * Applies all contexts to flow objective service.
+         */
+        public void apply() {
+            if (pendingContexts.isEmpty()) {
+                moveNextPendingToPending();
+            }
+            final Set<ObjectiveContext> contextsToApply = pendingContexts();
+            contextsToApply.forEach(ctx -> {
+                FlowObjectiveInstallationContext foiCtx =
+                        (FlowObjectiveInstallationContext) ctx;
+                flowObjectiveService.apply(foiCtx.deviceId, foiCtx.objective);
+            });
+        }
+
+        /**
+         * Gets all error contexts.
+         *
+         * @return the error contexts
+         */
+        public Set<ObjectiveContext> errorContexts() {
+            return ImmutableSet.copyOf(errorContexts);
+        }
+
+        /**
+         * Gets all pending contexts.
+         *
+         * @return the pending contexts
+         */
+        public Set<ObjectiveContext> pendingContexts() {
+            return ImmutableSet.copyOf(pendingContexts);
+        }
+
+        /**
+         * Gets all pending contexts of next stage.
+         *
+         * @return the pending contexts for next stage
+         */
+        public Set<ObjectiveContext> nextPendingContexts() {
+            return ImmutableSet.copyOf(nextPendingContexts);
+        }
+
+        /**
+         * Adds a context.
+         *
+         * @param context the context
+         */
+        public void addContext(ObjectiveContext context) {
+            Objects.requireNonNull(context);
+            contexts.add(context);
+        }
+
+        /**
+         * Adds a context to pending context of next stage.
+         *
+         * @param context the context
+         */
+        public void addNextPendingContext(ObjectiveContext context) {
+            Objects.requireNonNull(context);
+            nextPendingContexts.add(context);
+        }
+
+        /**
+         * Adds a context to pending context.
+         *
+         * @param context the context
+         */
+        public void addPendingContext(ObjectiveContext context) {
+            Objects.requireNonNull(context);
+            pendingContexts.add(context);
+        }
+
+        /**
+         * Removes the pending context.
+         *
+         * @param context the context
+         */
+        public void removePendingContext(ObjectiveContext context) {
+            Objects.requireNonNull(context);
+            pendingContexts.remove(context);
+        }
+
+        /**
+         * Moves pending context from next stage to current stage.
+         */
+        public void moveNextPendingToPending() {
+            pendingContexts.addAll(nextPendingContexts);
+            nextPendingContexts.clear();
+        }
+
+        /**
+         * Handles error of objective context.
+         *
+         * @param ctx the objective context
+         * @param error the error
+         */
+        public void handleObjectiveError(FlowObjectiveInstallationContext ctx,
+                                         ObjectiveError error) {
+            Objects.requireNonNull(ctx);
+            Objects.requireNonNull(error);
+            log.debug("Got error(s) when install objective: {}, error: {}, retry: {}",
+                      ctx.objective, ctx.error, ctx.retry);
+            if (ctx.retryTimes() > OBJECTIVE_RETRY_THRESHOLD) {
+                ctx.error = INSTALLATIONTHRESHOLDEXCEEDED;
+                pendingContexts.remove(ctx);
+                errorContexts.add(ctx);
+                return;
+            }
+            // reset error
+            ctx.error = null;
+            // strategies for errors
+            switch (error) {
+                case GROUPEXISTS:
+                    if (ctx.objective.op() == Objective.Operation.ADD &&
+                            ctx.objective instanceof NextObjective) {
+                        // Next group exists
+                        // build new objective with new op ADD_TO_EXIST
+                        NextObjective newObj =
+                                ((NextObjective.Builder) ctx.objective.copy()).addToExisting(ctx);
+                        ctx.setObjective(newObj, ctx.deviceId);
+                        ctx.increaseRetryValue();
+                        flowObjectiveService.apply(ctx.deviceId, ctx.objective);
+                    } else {
+                        pendingContexts.remove(ctx);
+                        errorContexts.add(ctx);
+                    }
+                    break;
+                case GROUPINSTALLATIONFAILED:
+                    // Group install failed, retry again
+                    ctx.increaseRetryValue();
+                    flowObjectiveService.apply(ctx.deviceId, ctx.objective);
+                    break;
+                case GROUPMISSING:
+                    if (ctx.objective.op() == Objective.Operation.ADD_TO_EXISTING) {
+                        // Next group not exist, but we want to add new buckets
+                        // build new objective with new op ADD
+                        NextObjective newObj = (NextObjective) ctx.objective.copy().add(ctx);
+                        ctx.setObjective(newObj, ctx.deviceId);
+                        ctx.increaseRetryValue();
+                        flowObjectiveService.apply(ctx.deviceId, ctx.objective);
+                    } else if (ctx.objective.op() == Objective.Operation.REMOVE ||
+                            ctx.objective.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
+                        // Already removed, no need to do anything
+                        ctx.error = null;
+                        pendingContexts.remove(ctx);
+                        return;
+                    } else {
+                        // Next chaining group missing, try again.
+                        ctx.increaseRetryValue();
+                        flowObjectiveService.apply(ctx.deviceId, ctx.objective);
+                    }
+                    break;
+                case FLOWINSTALLATIONFAILED:
+                case GROUPREMOVALFAILED:
+                case INSTALLATIONTIMEOUT:
+                    // Retry
+                    ctx.increaseRetryValue();
+                    flowObjectiveService.apply(ctx.deviceId, ctx.objective);
+                    break;
+                default:
+                    pendingContexts.remove(ctx);
+                    errorContexts.add(ctx);
+                    break;
+            }
+        }
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstaller.java b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstaller.java
new file mode 100644
index 0000000..f29ceec
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstaller.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.ObjectiveTrackerService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import static org.onosproject.net.intent.IntentInstaller.Direction.ADD;
+import static org.onosproject.net.intent.IntentInstaller.Direction.REMOVE;
+import static org.onosproject.net.intent.IntentState.INSTALLED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Installer for FlowRuleIntent.
+ */
+@Component(immediate = true)
+public class FlowRuleIntentInstaller implements IntentInstaller<FlowRuleIntent> {
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentExtensionService intentExtensionService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ObjectiveTrackerService trackerService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentInstallCoordinator intentInstallCoordinator;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+
+    @Activate
+    public void activate() {
+        intentExtensionService.registerInstaller(FlowRuleIntent.class, this);
+    }
+
+    @Deactivate
+    public void deactivated() {
+        intentExtensionService.unregisterInstaller(FlowRuleIntent.class);
+    }
+
+    protected final Logger log = getLogger(IntentManager.class);
+
+    @Override
+    public void apply(IntentOperationContext<FlowRuleIntent> context) {
+        Optional<IntentData> toUninstall = context.toUninstall();
+        Optional<IntentData> toInstall = context.toInstall();
+
+        List<FlowRuleIntent> uninstallIntents = context.intentsToUninstall();
+        List<FlowRuleIntent> installIntents = context.intentsToInstall();
+
+        if (!toInstall.isPresent() && !toUninstall.isPresent()) {
+            intentInstallCoordinator.intentInstallSuccess(context);
+            return;
+        } else if (!toInstall.isPresent()) {
+            // Uninstall only
+            trackIntentResources(toUninstall.get(), uninstallIntents, REMOVE);
+        } else if (!toUninstall.isPresent()) {
+            // Install only
+            trackIntentResources(toInstall.get(), installIntents, ADD);
+        } else {
+            IntentData uninstall = toUninstall.get();
+            IntentData install = toInstall.get();
+
+            // Filter out same intents and intents with same flow rules
+            Iterator<FlowRuleIntent> iterator = installIntents.iterator();
+            while (iterator.hasNext()) {
+                FlowRuleIntent installIntent = iterator.next();
+                uninstallIntents.stream().filter(uIntent -> {
+                    if (uIntent.equals(installIntent)) {
+                        return true;
+                    } else {
+                        return !flowRuleIntentChanged(uIntent, installIntent);
+                    }
+                }).findFirst().ifPresent(common -> {
+                    uninstallIntents.remove(common);
+                    if (INSTALLED.equals(uninstall.state())) {
+                        // only remove the install intent if the existing
+                        // intent (i.e. the uninstall one) is already
+                        // installed or installing
+                        iterator.remove();
+                    }
+                });
+            }
+            trackIntentResources(uninstall, uninstallIntents, REMOVE);
+            trackIntentResources(install, installIntents, ADD);
+        }
+
+        FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+        builder.newStage();
+
+        toUninstall.ifPresent(intentData -> {
+            uninstallIntents.stream().map(FlowRuleIntent::flowRules)
+                    .flatMap(Collection::stream).forEach(builder::remove);
+        });
+
+        toInstall.ifPresent(intentData -> {
+            installIntents.stream().map(FlowRuleIntent::flowRules)
+                    .flatMap(Collection::stream).forEach(builder::add);
+        });
+
+        FlowRuleOperationsContext flowRuleOperationsContext = new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                intentInstallCoordinator.intentInstallSuccess(context);
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                intentInstallCoordinator.intentInstallFailed(context);
+            }
+        };
+
+        FlowRuleOperations operations = builder.build(flowRuleOperationsContext);
+
+
+        log.debug("applying intent {} -> {} with {} rules: {}",
+                  toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
+                  toInstall.map(x -> x.key().toString()).orElse("<empty>"),
+                  operations.stages().stream().mapToLong(Set::size).sum(),
+                  operations.stages());
+        flowRuleService.apply(operations);
+    }
+
+    /**
+     * Track or un-track network resource of a Intent and it's installable
+     * Intents.
+     *
+     * @param intentData the Intent data
+     * @param intentsToApply the list of flow rule Intents from the Intent
+     * @param direction the direction to determine track or un-track
+     */
+    private void trackIntentResources(IntentData intentData, List<FlowRuleIntent> intentsToApply, Direction direction) {
+        switch (direction) {
+            case ADD:
+                trackerService.addTrackedResources(intentData.key(), intentData.intent().resources());
+                intentsToApply.forEach(installable ->
+                                               trackerService.addTrackedResources(intentData.key(),
+                                                                                  installable.resources()));
+                break;
+            default:
+                trackerService.removeTrackedResources(intentData.key(), intentData.intent().resources());
+                intentsToApply.forEach(installable ->
+                                               trackerService.removeTrackedResources(intentData.intent().key(),
+                                                                                     installable.resources()));
+                break;
+        }
+    }
+
+    /**
+     * Determines whether there is any flow rule changed
+     * (i.e., different set of flow rules or different treatments)
+     * between FlowRuleIntents to be uninstalled and to be installed.
+     *
+     * @param uninstallIntent FlowRuleIntent to uninstall
+     * @param installIntent   FlowRuleIntent to install
+     * @return true if flow rules which to be uninstalled contains all flow
+     *         rules which to be installed; false otherwise
+     */
+    private boolean flowRuleIntentChanged(FlowRuleIntent uninstallIntent,
+                                          FlowRuleIntent installIntent) {
+        Collection<FlowRule> flowRulesToUninstall = uninstallIntent.flowRules();
+        Collection<FlowRule> flowRulesToInstall = installIntent.flowRules();
+
+        // Check if any flow rule changed
+        for (FlowRule flowRuleToInstall : flowRulesToInstall) {
+            if (flowRulesToUninstall.stream().noneMatch(flowRuleToInstall::exactMatch)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstaller.java b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstaller.java
new file mode 100644
index 0000000..58c24e6
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstaller.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
+import org.onosproject.net.behaviour.protection.ProtectionConfig;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentException;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.ProtectionEndpointIntent;
+import org.onosproject.net.intent.impl.IntentManager;
+import org.onosproject.net.intent.impl.ObjectiveTrackerService;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.config.NetworkConfigEvent.Type.*;
+import static org.onosproject.net.intent.IntentInstaller.Direction.ADD;
+import static org.onosproject.net.intent.IntentInstaller.Direction.REMOVE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Installer for ProtectionEndpointIntent.
+ */
+@Component(immediate = true)
+public class ProtectionEndpointIntentInstaller implements IntentInstaller<ProtectionEndpointIntent> {
+    private static final String CONFIG_FAILED = "Config operation unsuccessful, expected %s, actual %s.";
+    private final Logger log = getLogger(IntentManager.class);
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentExtensionService intentExtensionService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    NetworkConfigService networkConfigService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    IntentInstallCoordinator intentInstallCoordinator;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    ObjectiveTrackerService trackerService;
+
+    @Activate
+    public void activate() {
+        intentExtensionService.registerInstaller(ProtectionEndpointIntent.class, this);
+    }
+
+    @Deactivate
+    public void deactivated() {
+        intentExtensionService.unregisterInstaller(ProtectionEndpointIntent.class);
+    }
+
+    @Override
+    public void apply(IntentOperationContext<ProtectionEndpointIntent> context) {
+        Optional<IntentData> toUninstall = context.toUninstall();
+        Optional<IntentData> toInstall = context.toInstall();
+
+        List<ProtectionEndpointIntent> uninstallIntents = context.intentsToUninstall();
+        List<ProtectionEndpointIntent> installIntents = context.intentsToInstall();
+
+        if (!toInstall.isPresent() && !toUninstall.isPresent()) {
+            intentInstallCoordinator.intentInstallSuccess(context);
+            return;
+        }
+
+        if (toUninstall.isPresent()) {
+            IntentData intentData = toUninstall.get();
+            trackerService.removeTrackedResources(intentData.key(), intentData.intent().resources());
+            uninstallIntents.forEach(installable ->
+                                             trackerService.removeTrackedResources(intentData.intent().key(),
+                                                                                   installable.resources()));
+        }
+
+        if (toInstall.isPresent()) {
+            IntentData intentData = toInstall.get();
+            trackerService.addTrackedResources(intentData.key(), intentData.intent().resources());
+            installIntents.forEach(installable ->
+                                           trackerService.addTrackedResources(intentData.key(),
+                                                                              installable.resources()));
+        }
+
+        List<Stage> stages = new ArrayList<>();
+
+        stages.add(new Stage(uninstallIntents.stream()
+                                     .map(i -> Pair.of(i, REMOVE))
+                                     .collect(Collectors.toList())));
+
+        stages.add(new Stage(installIntents.stream()
+                                     .map(i -> Pair.of(i, ADD))
+                                     .collect(Collectors.toList())));
+        for (Stage stage : stages) {
+            log.debug("applying Stage {}", stage);
+            try {
+                // wait for stage completion
+                stage.apply();
+                stage.listeners().forEach(networkConfigService::removeListener);
+            } catch (IntentException e) {
+                log.error("Stage {} failed, reason: {}", stage, e.toString());
+                intentInstallCoordinator.intentInstallFailed(context);
+                return;
+            }
+        }
+        // All stage success
+        intentInstallCoordinator.intentInstallSuccess(context);
+    }
+
+    /**
+     * Stage of installable Intents which can be processed in parallel.
+     */
+    private final class Stage {
+        // should it have progress state, how far it went?
+        private final Collection<Pair<ProtectionEndpointIntent, Direction>> ops;
+        private final Set<NetworkConfigListener> listeners = Sets.newHashSet();
+
+        /**
+         * Create a stage with given operations.
+         *
+         * @param ops the operations
+         */
+        Stage(Collection<Pair<ProtectionEndpointIntent, Direction>> ops) {
+            this.ops = checkNotNull(ops);
+        }
+
+        /**
+         * Applies all operations for this stage.
+         *
+         * @return the CompletableFuture object for this operation
+         */
+        void apply() {
+            ops.stream()
+                    .map(op -> applyOp(op.getRight(), op.getLeft()))
+                    .forEach(future -> {
+                        try {
+                            future.get(100, TimeUnit.MILLISECONDS);
+                        } catch (TimeoutException | InterruptedException | ExecutionException e) {
+                            throw new IntentException(e.toString());
+                        }
+                    });
+        }
+
+        /**
+         * Applies the protection endpoint Intent with a given direction.
+         *
+         * @param dir the direction
+         * @param intent the protection endpoint Intent
+         * @return the CompletableFuture object for this operation
+         */
+        private CompletableFuture<Void> applyOp(Direction dir, ProtectionEndpointIntent intent) {
+            log.trace("applying {}: {}", dir, intent);
+            if (dir == REMOVE) {
+                ProtectionConfigListener listener =
+                        new ProtectionConfigListener(ImmutableSet.of(CONFIG_REMOVED),
+                                                     intent.deviceId());
+                networkConfigService.addListener(listener);
+                listeners.add(listener);
+                networkConfigService.removeConfig(intent.deviceId(), ProtectionConfig.class);
+                return listener.completableFuture();
+            } else {
+                ProtectedTransportEndpointDescription description = intent.description();
+
+                // Can't do following. Will trigger empty CONFIG_ADDED
+                //ProtectionConfig cfg = networkConfigService.addConfig(intent.deviceId(),
+                //                                                      ProtectionConfig.class);
+                ProtectionConfig cfg = new ProtectionConfig(intent.deviceId());
+                cfg.fingerprint(description.fingerprint());
+                cfg.peer(description.peer());
+                cfg.paths(description.paths());
+                ProtectionConfigListener listener =
+                        new ProtectionConfigListener(ImmutableSet.of(CONFIG_ADDED, CONFIG_UPDATED),
+                                                     intent.deviceId());
+
+                networkConfigService.addListener(listener);
+                listeners.add(listener);
+                networkConfigService.applyConfig(intent.deviceId(),
+                                                 ProtectionConfig.class,
+                                                 cfg.node());
+                return listener.completableFuture();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return ops.toString();
+        }
+
+        public Set<NetworkConfigListener> listeners() {
+            return listeners;
+        }
+
+        /**
+         * Listener for protection config for specific config event and device.
+         */
+        class ProtectionConfigListener implements NetworkConfigListener {
+            private CompletableFuture<Void> completableFuture;
+            private Set<NetworkConfigEvent.Type> listenTypes;
+            private DeviceId listenDevice;
+
+            public ProtectionConfigListener(Set<NetworkConfigEvent.Type> listenTypes, DeviceId listenDevice) {
+                completableFuture = new CompletableFuture<>();
+                this.listenTypes = listenTypes;
+                this.listenDevice = listenDevice;
+            }
+
+            @Override
+            public void event(NetworkConfigEvent event) {
+                if (!event.subject().equals(listenDevice)) {
+                    return;
+                }
+                if (!listenTypes.contains(event.type())) {
+                    String errorMsg = String.format(CONFIG_FAILED, listenTypes.toString(), event.type());
+                    completableFuture.completeExceptionally(new IntentException(errorMsg));
+                } else {
+                    completableFuture.complete(null);
+                }
+            }
+
+            public CompletableFuture<Void> completableFuture() {
+                return completableFuture;
+            }
+        }
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/installer/package-info.java b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/package-info.java
new file mode 100644
index 0000000..b971165
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/installer/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present 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.
+ */
+
+/**
+ * Installers for different installable Intents.
+ */
+package org.onosproject.net.intent.impl.installer;
\ No newline at end of file
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/InstallCoordinatorTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/InstallCoordinatorTest.java
new file mode 100644
index 0000000..2bdf200
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/InstallCoordinatorTest.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017-present 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.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestTools;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.TestInstallableIntent;
+import org.onosproject.store.intent.impl.IntentStoreAdapter;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.IntStream;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for install coordinator.
+ */
+public class InstallCoordinatorTest {
+    private static final int INSTALL_DELAY = 100;
+    private static final int INSTALL_DURATION = 1000;
+    private static final IdGenerator ID_GENERATOR = new MockIdGenerator();
+
+    private InstallCoordinator installCoordinator;
+    private InstallerRegistry installerRegistry;
+    private TestIntentStore intentStore;
+    private TestIntentInstaller intentInstaller;
+
+
+    @Before
+    public void setup() {
+        Intent.unbindIdGenerator(ID_GENERATOR);
+        Intent.bindIdGenerator(ID_GENERATOR);
+        installerRegistry = new InstallerRegistry();
+        intentStore = new TestIntentStore();
+        intentInstaller = new TestIntentInstaller();
+        installerRegistry.registerInstaller(TestInstallableIntent.class, intentInstaller);
+
+        installCoordinator = new InstallCoordinator(installerRegistry, intentStore);
+    }
+
+    @After
+    public void tearDown() {
+        installerRegistry.unregisterInstaller(TestInstallableIntent.class);
+        Intent.unbindIdGenerator(ID_GENERATOR);
+    }
+
+    /**
+     * Installs test Intents.
+     */
+    @Test
+    public void testInstallIntent() {
+        IntentData toInstall = new IntentData(createTestIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        List<Intent> intents = Lists.newArrayList();
+        IntStream.range(0, 10).forEach(val -> {
+            intents.add(new TestInstallableIntent(val));
+        });
+        toInstall = new IntentData(toInstall, intents);
+        installCoordinator.installIntents(Optional.empty(), Optional.of(toInstall));
+        Intent toInstallIntent = toInstall.intent();
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toInstallIntent, newData.intent());
+            assertEquals(IntentState.INSTALLED, newData.state());
+            assertEquals(intents, newData.installables());
+        });
+    }
+
+    /**
+     * Uninstalls test Intents.
+     */
+    @Test
+    public void testUninstallIntent() {
+        IntentData toUninstall = new IntentData(createTestIntent(),
+                                              IntentState.WITHDRAWING,
+                                              new WallClockTimestamp());
+        List<Intent> intents = Lists.newArrayList();
+
+        IntStream.range(0, 10).forEach(val -> {
+            intents.add(new TestInstallableIntent(val));
+        });
+
+        toUninstall = new IntentData(toUninstall, intents);
+
+        installCoordinator.installIntents(Optional.of(toUninstall), Optional.empty());
+        Intent toUninstallIntent = toUninstall.intent();
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toUninstallIntent, newData.intent());
+            assertEquals(IntentState.WITHDRAWN, newData.state());
+            assertEquals(ImmutableList.of(), newData.installables());
+        });
+
+    }
+
+    /**
+     * Do both uninstall and install test Intents.
+     */
+    @Test
+    public void testUninstallAndInstallIntent() {
+        IntentData toUninstall = new IntentData(createTestIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        IntentData toInstall = new IntentData(createTestIntent(),
+                                                IntentState.INSTALLING,
+                                                new WallClockTimestamp());
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+
+        IntStream.range(0, 10).forEach(val -> {
+            intentsToUninstall.add(new TestInstallableIntent(val));
+        });
+
+        IntStream.range(10, 20).forEach(val -> {
+            intentsToInstall.add(new TestInstallableIntent(val));
+        });
+
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+        installCoordinator.installIntents(Optional.of(toUninstall), Optional.of(toInstall));
+        Intent toInstallIntent = toInstall.intent();
+
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toInstallIntent, newData.intent());
+            assertEquals(IntentState.INSTALLED, newData.state());
+            assertEquals(intentsToInstall, newData.installables());
+        });
+    }
+
+    /**
+     * Not uninstall nor install anything.
+     */
+    @Test
+    public void testInstallNothing() {
+        installCoordinator.installIntents(Optional.empty(), Optional.empty());
+        assertNull(intentStore.newData);
+    }
+
+    /**
+     * Test Intent install failed.
+     */
+    @Test
+    public void testInstallFailed() {
+        installerRegistry.unregisterInstaller(TestInstallableIntent.class);
+        installerRegistry.registerInstaller(TestInstallableIntent.class, new TestFailedIntentInstaller());
+        IntentData toUninstall = new IntentData(createTestIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        IntentData toInstall = new IntentData(createTestIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+
+        IntStream.range(0, 10).forEach(val -> {
+            intentsToUninstall.add(new TestInstallableIntent(val));
+        });
+
+        IntStream.range(10, 20).forEach(val -> {
+            intentsToInstall.add(new TestInstallableIntent(val));
+        });
+
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+        installCoordinator.installIntents(Optional.of(toUninstall), Optional.of(toInstall));
+
+        Intent toUninstallIntent = toUninstall.intent();
+        TestTools.assertAfter(INSTALL_DELAY, INSTALL_DURATION, () -> {
+            IntentData newData = intentStore.newData;
+            assertEquals(toUninstallIntent, newData.intent());
+            assertEquals(IntentState.CORRUPT, newData.state());
+            assertEquals(intentsToUninstall, newData.installables());
+        });
+    }
+
+    /**
+     * Creates a test Intent.
+     *
+     * @return the test Intent.
+     */
+    private PointToPointIntent createTestIntent() {
+        FilteredConnectPoint ingress = new FilteredConnectPoint(ConnectPoint.deviceConnectPoint("s1/1"));
+        FilteredConnectPoint egress = new FilteredConnectPoint(ConnectPoint.deviceConnectPoint("s1/2"));
+        ApplicationId appId = TestApplicationId.create("test App");
+        return PointToPointIntent.builder()
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .appId(appId)
+                .key(Key.of("Test key", appId))
+                .build();
+    }
+
+    /**
+     * Test Intent store; records the newest Intent data.
+     */
+    class TestIntentStore extends IntentStoreAdapter {
+        IntentData newData;
+        @Override
+        public void write(IntentData newData) {
+            this.newData = newData;
+        }
+    }
+
+    /**
+     * Test Intent installer; always success for every Intent operation.
+     */
+    class TestIntentInstaller implements IntentInstaller<TestInstallableIntent> {
+        @Override
+        public void apply(IntentOperationContext<TestInstallableIntent> context) {
+            installCoordinator.success(context);
+        }
+    }
+
+    /**
+     * Test Intent installer; always failed for every Intent operation.
+     */
+    class TestFailedIntentInstaller implements IntentInstaller<TestInstallableIntent> {
+        @Override
+        public void apply(IntentOperationContext<TestInstallableIntent> context) {
+            installCoordinator.failed(context);
+        }
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
index 585d3a0..45c90e7 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
@@ -33,6 +33,9 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.impl.TestCoreManager;
 import org.onosproject.net.NetworkResource;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompilationException;
@@ -42,7 +45,10 @@
 import org.onosproject.net.intent.IntentEvent.Type;
 import org.onosproject.net.intent.IntentExtensionService;
 import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentInstaller;
 import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentOperationContext;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
@@ -93,8 +99,11 @@
 
     protected IntentService service;
     protected IntentExtensionService extensionService;
+    protected IntentInstallCoordinator intentInstallCoordinator;
     protected TestListener listener = new TestListener();
     protected TestIntentCompiler compiler = new TestIntentCompiler();
+    protected TestIntentInstaller installer;
+    protected TestIntentTracker trackerService = new TestIntentTracker();
 
     private static class TestListener implements IntentListener {
         final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
@@ -217,6 +226,56 @@
         }
     }
 
+    public static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
+
+        protected IntentExtensionService intentExtensionService;
+        protected ObjectiveTrackerService trackerService;
+        protected IntentInstallCoordinator intentInstallCoordinator;
+        protected FlowRuleService flowRuleService;
+
+        public TestIntentInstaller(IntentExtensionService intentExtensionService,
+                                   ObjectiveTrackerService trackerService,
+                                   IntentInstallCoordinator intentInstallCoordinator,
+                                   FlowRuleService flowRuleService) {
+            this.intentExtensionService = intentExtensionService;
+            this.trackerService = trackerService;
+            this.intentInstallCoordinator = intentInstallCoordinator;
+            this.flowRuleService = flowRuleService;
+        }
+
+        @Override
+        public void apply(IntentOperationContext<MockInstallableIntent> context) {
+            List<MockInstallableIntent> uninstallIntents = context.intentsToUninstall();
+            List<MockInstallableIntent> installIntents = context.intentsToInstall();
+
+            FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+
+            uninstallIntents.stream()
+                    .map(FlowRuleIntent::flowRules)
+                    .flatMap(Collection::stream)
+                    .forEach(builder::remove);
+
+            installIntents.stream()
+                    .map(FlowRuleIntent::flowRules)
+                    .flatMap(Collection::stream)
+                    .forEach(builder::add);
+
+            FlowRuleOperationsContext ctx = new FlowRuleOperationsContext() {
+                @Override
+                public void onSuccess(FlowRuleOperations ops) {
+                    intentInstallCoordinator.intentInstallSuccess(context);
+                }
+
+                @Override
+                public void onError(FlowRuleOperations ops) {
+                    intentInstallCoordinator.intentInstallFailed(context);
+                }
+            };
+
+            flowRuleService.apply(builder.build(ctx));
+        }
+    }
+
     private static EntryForIntentMatcher hasIntentWithId(IntentId id) {
         return new EntryForIntentMatcher(id);
     }
@@ -227,17 +286,24 @@
         flowRuleService = new MockFlowRuleService();
         manager.store = new SimpleIntentStore();
         injectEventDispatcher(manager, new TestEventDispatcher());
-        manager.trackerService = new TestIntentTracker();
+        manager.trackerService = trackerService;
         manager.flowRuleService = flowRuleService;
         manager.coreService = new TestCoreManager();
         manager.configService = mock(ComponentConfigService.class);
         service = manager;
         extensionService = manager;
+        intentInstallCoordinator = manager;
+
 
         manager.activate();
         service.addListener(listener);
         extensionService.registerCompiler(MockIntent.class, compiler);
 
+        installer = new TestIntentInstaller(extensionService, trackerService,
+                                            intentInstallCoordinator, flowRuleService);
+
+        extensionService.registerInstaller(MockInstallableIntent.class, installer);
+
         assertTrue("store should be empty",
                    Sets.newHashSet(service.getIntents()).isEmpty());
         assertEquals(0L, flowRuleService.getFlowRuleCount());
@@ -269,6 +335,7 @@
     @After
     public void tearDown() {
         extensionService.unregisterCompiler(MockIntent.class);
+        extensionService.unregisterInstaller(MockInstallableIntent.class);
         service.removeListener(listener);
         manager.deactivate();
         // TODO null the other refs?
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/AbstractIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/AbstractIntentInstallerTest.java
new file mode 100644
index 0000000..caa07be
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/AbstractIntentInstallerTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentInstallCoordinator;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.impl.ObjectiveTrackerService;
+
+import static org.easymock.EasyMock.createMock;
+
+/**
+ * Abstract class to hold the common variables and pieces of code for Intent
+ * installer test.
+ */
+public class AbstractIntentInstallerTest {
+    protected static final ApplicationId APP_ID = TestApplicationId.create("IntentInstallerTest");
+    protected static final ConnectPoint CP1 = ConnectPoint.deviceConnectPoint("s1/1");
+    protected static final ConnectPoint CP2 = ConnectPoint.deviceConnectPoint("s1/2");
+    protected static final ConnectPoint CP3 = ConnectPoint.deviceConnectPoint("s1/3");
+    protected static final Key KEY1 = Key.of("test intent 1", APP_ID);
+    protected static final ResourceGroup RG1 = ResourceGroup.of("test resource group 1");
+    protected static final int DEFAULT_PRIORITY = 30000;
+    protected static final IdGenerator ID_GENERATOR = new MockIdGenerator();
+
+    protected IntentExtensionService intentExtensionService;
+    protected ObjectiveTrackerService trackerService;
+    protected TestIntentInstallCoordinator intentInstallCoordinator;
+
+    public void setup() {
+        Intent.unbindIdGenerator(ID_GENERATOR);
+        Intent.bindIdGenerator(ID_GENERATOR);
+        intentExtensionService = createMock(IntentExtensionService.class);
+        trackerService = createMock(ObjectiveTrackerService.class);
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+    }
+
+    public void tearDown() {
+        Intent.unbindIdGenerator(ID_GENERATOR);
+    }
+
+    /**
+     * Creates point to point Intent for test.
+     *
+     * @return the point to point Intent
+     */
+    public PointToPointIntent createP2PIntent() {
+        PointToPointIntent intent;
+        TrafficSelector selector = DefaultTrafficSelector.emptySelector();
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+        FilteredConnectPoint ingress = new FilteredConnectPoint(CP1);
+        FilteredConnectPoint egress = new FilteredConnectPoint(CP2);
+
+        intent = PointToPointIntent.builder()
+                .selector(selector)
+                .treatment(treatment)
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .appId(APP_ID)
+                .build();
+
+        return intent;
+    }
+
+    /**
+     * The Intent install coordinator for test.
+     * Records success and fail context.
+     */
+    class TestIntentInstallCoordinator implements IntentInstallCoordinator {
+
+        IntentOperationContext successContext;
+        IntentOperationContext failedContext;
+
+        @Override
+        public void intentInstallSuccess(IntentOperationContext context) {
+            successContext = context;
+        }
+
+        @Override
+        public void intentInstallFailed(IntentOperationContext context) {
+            failedContext = context;
+        }
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/DomainIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/DomainIntentInstallerTest.java
new file mode 100644
index 0000000..2542322
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/DomainIntentInstallerTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.domain.DomainIntent;
+import org.onosproject.net.domain.DomainIntentOperations;
+import org.onosproject.net.domain.DomainIntentService;
+import org.onosproject.net.domain.DomainPointToPointIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for domain Intent installer.
+ */
+public class DomainIntentInstallerTest extends AbstractIntentInstallerTest {
+    protected DomainIntentInstaller installer;
+    protected TestDomainIntentService domainIntentService;
+
+    @Before
+    public void setup() {
+        super.setup();
+        domainIntentService = new TestDomainIntentService();
+        installer = new DomainIntentInstaller();
+        installer.domainIntentService = domainIntentService;
+        installer.trackerService = trackerService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+
+        installer.activated();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs domain Intents.
+     */
+    @Test
+    public void testInstall() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createDomainIntents();
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Uninstall domain Intents.
+     */
+    @Test
+    public void testUninstall() {
+        List<Intent> intentsToUninstall = createDomainIntents();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.WITHDRAWING,
+                                                new WallClockTimestamp());
+        IntentData toInstall = null;
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Do both uninstall and install domain Intents.
+     */
+    @Test
+    public void testUninstallAndInstall() {
+        List<Intent> intentsToUninstall = createDomainIntents();
+        List<Intent> intentsToInstall = createAnotherDomainIntents();
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Test if domain Intent installation operations failed.
+     */
+    @Test
+    public void testInstallFailed() {
+        domainIntentService = new TestFailedDomainIntentService();
+        installer.domainIntentService = domainIntentService;
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createDomainIntents();
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<DomainIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.failedContext, operationContext);
+    }
+
+    /**
+     * Creates domain Intents.
+     *
+     * @return the domain Intents
+     */
+    private List<Intent> createDomainIntents() {
+        FilteredConnectPoint ingress = new FilteredConnectPoint(CP1);
+        FilteredConnectPoint egress = new FilteredConnectPoint(CP2);
+        DomainPointToPointIntent intent = DomainPointToPointIntent.builder()
+                .appId(APP_ID)
+                .key(KEY1)
+                .priority(DEFAULT_PRIORITY)
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .links(ImmutableList.of())
+                .build();
+
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Create another domain Intents.
+     *
+     * @return the domain Intents
+     */
+    private List<Intent> createAnotherDomainIntents() {
+        FilteredConnectPoint ingress = new FilteredConnectPoint(CP1);
+        FilteredConnectPoint egress = new FilteredConnectPoint(CP3);
+        DomainPointToPointIntent intent = DomainPointToPointIntent.builder()
+                .appId(APP_ID)
+                .key(KEY1)
+                .priority(DEFAULT_PRIORITY)
+                .filteredIngressPoint(ingress)
+                .filteredEgressPoint(egress)
+                .links(ImmutableList.of())
+                .build();
+
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Test domain Intent service; always success for all domain Intent operations.
+     */
+    class TestDomainIntentService implements DomainIntentService {
+        @Override
+        public void sumbit(DomainIntentOperations domainOperations) {
+            domainOperations.callback().onSuccess(domainOperations);
+        }
+    }
+
+    /**
+     * Test domain Intent service; always failed of any domain Intent operation.
+     */
+    class TestFailedDomainIntentService extends TestDomainIntentService {
+
+        @Override
+        public void sumbit(DomainIntentOperations domainOperations) {
+            domainOperations.callback().onError(domainOperations);
+        }
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstallerTest.java
new file mode 100644
index 0000000..99f5db9
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowObjectiveIntentInstallerTest.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveServiceAdapter;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.intent.FlowObjectiveIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+import static org.onosproject.net.flowobjective.ObjectiveError.*;
+
+/**
+ * Tests for flow objective Intent installer.
+ */
+public class FlowObjectiveIntentInstallerTest extends AbstractIntentInstallerTest {
+    private static final int NEXT_ID_1 = 1;
+    protected FlowObjectiveIntentInstaller installer;
+    protected TestFlowObjectiveService flowObjectiveService;
+
+    @Before
+    public void setup() {
+        super.setup();
+        flowObjectiveService = new TestFlowObjectiveService();
+        installer = new FlowObjectiveIntentInstaller();
+        installer.flowObjectiveService = flowObjectiveService;
+        installer.trackerService = trackerService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+
+        installer.activate();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs flow objective Intents.
+     */
+    @Test
+    public void testInstallIntent() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowObjectiveIntents();
+
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Uninstalls flow objective Intents.
+     */
+    @Test
+    public void testUninstallIntent() {
+        List<Intent> intentsToUninstall = createFlowObjectiveIntents();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+
+
+        IntentData toInstall = null;
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                              IntentState.WITHDRAWING,
+                                              new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Do both uninstall and install flow objective Intents.
+     */
+    @Test
+    public void testUninstallAndInstallIntent() {
+        List<Intent> intentsToUninstall = createFlowObjectiveIntents();
+        List<Intent> intentsToInstall = createAnotherFlowObjectiveIntents();
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<FlowObjectiveIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /*
+     * Error handling
+     */
+    IntentOperationContext context;
+    IntentOperationContext failedContext;
+    IntentOperationContext successContext;
+    List<ObjectiveError> errors;
+
+    /**
+     * Handles UNSUPPORTED error.
+     */
+    @Test
+    public void testUnsupportedError() {
+        // Unsupported, should just failed
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService();
+        context = createInstallContext();
+        installer.apply(context);
+        assertEquals(intentInstallCoordinator.failedContext, context);
+    }
+
+    /**
+     * Handles FLOWINSTALLATIONFAILED error with touch the threshold.
+     */
+    @Test
+    public void testFlowInstallationFailedError() {
+        // flow install failed, should retry until retry threshold
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED,
+                                  FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED,
+                                  FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED,
+                                  FLOWINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        failedContext = intentInstallCoordinator.failedContext;
+        assertEquals(failedContext, context);
+    }
+
+    /**
+     * Handles FLOWINSTALLATIONFAILED error without touch the threshold.
+     */
+    @Test
+    public void testFlowInstallationFailedErrorUnderThreshold() {
+        // And retry two times and success
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(FLOWINSTALLATIONFAILED, FLOWINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPINSTALLATIONFAILED error with touch the threshold.
+     */
+    @Test
+    public void testGroupInstallationFailedError() {
+        // Group install failed, and retry threshold exceed
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED,
+                                  GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED,
+                                  GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED,
+                                  GROUPINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        failedContext = intentInstallCoordinator.failedContext;
+        assertEquals(failedContext, context);
+
+    }
+
+    /**
+     * Handles GROUPINSTALLATIONFAILED error without touch the threshold.
+     */
+    @Test
+    public void testGroupInstallationFailedErrorUnderThreshold() {
+        // group install failed, and retry two times.
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPINSTALLATIONFAILED, GROUPINSTALLATIONFAILED);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+
+    }
+
+    /**
+     * Handles GROUPEXISTS error.
+     */
+    @Test
+    public void testGroupExistError() {
+        // group exists, retry by using add to exist
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPEXISTS);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPMISSING error with ADD_TO_EXIST operation.
+     */
+    @Test
+    public void testGroupMissingError() {
+        // group exist -> group missing -> add group
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPEXISTS, GROUPMISSING);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPMISSING error with ADD operation.
+     */
+    @Test
+    public void testGroupChainElementMissingError() {
+        // group chain element missing
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPMISSING);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createInstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Handles GROUPMISSING error with REMOVE operation.
+     */
+    @Test
+    public void testGroupAlreadyRemoved() {
+        // group already removed
+        intentInstallCoordinator = new TestIntentInstallCoordinator();
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        errors = ImmutableList.of(GROUPMISSING);
+        installer.flowObjectiveService = new TestFailedFlowObjectiveService(errors);
+        context = createUninstallContext();
+        installer.apply(context);
+        successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, context);
+    }
+
+    /**
+     * Creates Intent operation context for uninstall Intents.
+     *
+     * @return the context
+     */
+    private IntentOperationContext createUninstallContext() {
+        List<Intent> intentsToUninstall = createFlowObjectiveIntents();
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        IntentData toInstall = null;
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        return new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+    }
+
+    /**
+     * Creates Intent operation context for install Intents.
+     *
+     * @return the context
+     */
+    private IntentOperationContext createInstallContext() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowObjectiveIntents();
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        return new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+    }
+
+    /**
+     * Creates flow objective Intents.
+     *
+     * @return the flow objective intents
+     */
+    private List<Intent> createFlowObjectiveIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FilteringObjective filt = DefaultFilteringObjective.builder()
+                .addCondition(selector.getCriterion(Criterion.Type.IN_PORT))
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .permit()
+                .add();
+
+        NextObjective next = DefaultNextObjective.builder()
+                .withMeta(selector)
+                .addTreatment(treatment)
+                .makePermanent()
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .withType(NextObjective.Type.SIMPLE)
+                .withId(NEXT_ID_1)
+                .add();
+
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .nextStep(NEXT_ID_1)
+                .add();
+
+        List<Objective> objectives = ImmutableList.of(filt, next, fwd);
+        List<DeviceId> deviceIds = ImmutableList.of(CP1.deviceId(), CP1.deviceId(), CP1.deviceId());
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        Intent intent = new FlowObjectiveIntent(APP_ID, KEY1, deviceIds, objectives, resources, RG1);
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Creates flow objective Intents with different selector.
+     *
+     * @return the flow objective Intents
+     */
+    private List<Intent> createAnotherFlowObjectiveIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VlanId.vlanId("100"))
+                .matchInPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FilteringObjective filt = DefaultFilteringObjective.builder()
+                .addCondition(selector.getCriterion(Criterion.Type.IN_PORT))
+                .addCondition(selector.getCriterion(Criterion.Type.VLAN_VID))
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .permit()
+                .add();
+
+        NextObjective next = DefaultNextObjective.builder()
+                .withMeta(selector)
+                .addTreatment(treatment)
+                .makePermanent()
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(APP_ID)
+                .withType(NextObjective.Type.SIMPLE)
+                .withId(NEXT_ID_1)
+                .add();
+
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .nextStep(NEXT_ID_1)
+                .add();
+
+        List<Objective> objectives = ImmutableList.of(filt, next, fwd);
+        List<DeviceId> deviceIds = ImmutableList.of(CP1.deviceId(), CP1.deviceId(), CP1.deviceId());
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        Intent intent = new FlowObjectiveIntent(APP_ID, KEY1, deviceIds, objectives, resources, RG1);
+        return ImmutableList.of(intent);
+    }
+
+    /**
+     * Flow objective service for test; always successful for every flow objectives.
+     */
+    class TestFlowObjectiveService extends FlowObjectiveServiceAdapter {
+        List<DeviceId> devices = Lists.newArrayList();
+        List<Objective> objectives = Lists.newArrayList();
+
+        @Override
+        public void apply(DeviceId deviceId, Objective objective) {
+            devices.add(deviceId);
+            objectives.add(objective);
+            objective.context().ifPresent(context -> context.onSuccess(objective));
+        }
+    }
+
+    /**
+     * Flow objective service for test; contains errors for every flow objective
+     * submission.
+     */
+    class TestFailedFlowObjectiveService extends TestFlowObjectiveService {
+        private final Set<ObjectiveError> groupErrors =
+                ImmutableSet.of(GROUPEXISTS, GROUPINSTALLATIONFAILED,
+                                GROUPMISSING, GROUPREMOVALFAILED);
+
+        /**
+         * Error states to test error handler by given error queue
+         * e.g.
+         * FLOWINSTALLATIONFAILED -> FLOWINSTALLATIONFAILED -> null: should be success
+         * FLOWINSTALLATIONFAILED -> five same error -> ....       : should be failed
+         */
+        List<ObjectiveError> errors;
+
+        public TestFailedFlowObjectiveService() {
+            errors = Lists.newArrayList();
+            errors.add(UNSUPPORTED);
+        }
+
+        public TestFailedFlowObjectiveService(List<ObjectiveError> errors) {
+            this.errors = Lists.newArrayList(errors);
+        }
+
+        @Override
+        public void apply(DeviceId deviceId, Objective objective) {
+            if (errors.size() != 0) {
+                if (groupErrors.contains(errors.get(0)) && objective instanceof NextObjective) {
+                    ObjectiveError error = errors.remove(0);
+                    objective.context().ifPresent(context -> context.onError(objective, error));
+                    return;
+                }
+                if (!groupErrors.contains(errors.get(0)) && !(objective instanceof NextObjective)) {
+                    ObjectiveError error = errors.remove(0);
+                    objective.context().ifPresent(context -> context.onError(objective, error));
+                    return;
+                }
+            }
+            objective.context().ifPresent(context -> context.onSuccess(objective));
+
+        }
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java
new file mode 100644
index 0000000..418392b
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleServiceAdapter;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for flow rule Intent installer.
+ */
+public class FlowRuleIntentInstallerTest extends AbstractIntentInstallerTest {
+
+    private TestFlowRuleService flowRuleService;
+    private FlowRuleIntentInstaller installer;
+
+    @Before
+    public void setup() {
+        super.setup();
+        flowRuleService = new TestFlowRuleService();
+        installer = new FlowRuleIntentInstaller();
+        installer.flowRuleService = flowRuleService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        installer.trackerService = trackerService;
+
+        installer.activate();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs Intents only, no Intents to be uninstall.
+     */
+    @Test
+    public void testInstallOnly() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        Set<FlowRule> expectedFlowRules = intentsToInstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesAdd);
+    }
+
+    /**
+     * Uninstalls Intents only, no Intents to be install.
+     */
+    @Test
+    public void testUninstallOnly() {
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        List<Intent> intentsToUninstall = createFlowRuleIntents();
+
+        IntentData toInstall = null;
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                              IntentState.WITHDRAWING,
+                                              new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        Set<FlowRule> expectedFlowRules = intentsToUninstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesRemove);
+    }
+
+    /**
+     * Do both install and uninstall Intents with different flow rules.
+     */
+    @Test
+    public void testUninstallAndInstall() {
+        List<Intent> intentsToInstall = createAnotherFlowRuleIntents();
+        List<Intent> intentsToUninstall = createFlowRuleIntents();
+
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        Set<FlowRule> expectedFlowRules = intentsToUninstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesRemove);
+
+        expectedFlowRules = intentsToInstall.stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .map(FlowRuleIntent::flowRules)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        assertEquals(expectedFlowRules, flowRuleService.flowRulesAdd);
+    }
+
+    /**
+     * Do both install and uninstall Intents with same flow rules.
+     */
+    @Test
+    public void testUninstallAndInstallUnchanged() {
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+        List<Intent> intentsToUninstall = createFlowRuleIntents();
+
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        assertEquals(0, flowRuleService.flowRulesRemove.size());
+        assertEquals(0, flowRuleService.flowRulesAdd.size());
+    }
+
+    /**
+     * Do both install and uninstall Intents with same flow rule Intent.
+     */
+    @Test
+    public void testUninstallAndInstallSame() {
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+        List<Intent> intentsToUninstall = intentsToInstall;
+
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        assertEquals(0, flowRuleService.flowRulesRemove.size());
+        assertEquals(0, flowRuleService.flowRulesAdd.size());
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+
+        assertEquals(0, flowRuleService.flowRulesRemove.size());
+        assertEquals(0, flowRuleService.flowRulesAdd.size());
+    }
+
+    /**
+     * Test if the flow installation failed.
+     */
+    @Test
+    public void testFailed() {
+        installer.flowRuleService = new TestFailedFlowRuleService();
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createFlowRuleIntents();
+
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        IntentOperationContext failedContext = intentInstallCoordinator.failedContext;
+        assertEquals(failedContext, operationContext);
+    }
+
+    /**
+     * Generates FlowRuleIntents for test.
+     *
+     * @return the FlowRuleIntents for test
+     */
+    public List<Intent> createFlowRuleIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPhyPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .forDevice(CP1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .build();
+
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        FlowRuleIntent intent = new FlowRuleIntent(APP_ID,
+                                                   KEY1,
+                                                   ImmutableList.of(flowRule),
+                                                   resources,
+                                                   PathIntent.ProtectionType.PRIMARY,
+                                                   RG1);
+
+        List<Intent> flowRuleIntents = Lists.newArrayList();
+        flowRuleIntents.add(intent);
+
+        return flowRuleIntents;
+    }
+
+    /**
+     * Generates another different FlowRuleIntents for test.
+     *
+     * @return the FlowRuleIntents for test
+     */
+    public List<Intent> createAnotherFlowRuleIntents() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VlanId.vlanId("100"))
+                .matchInPhyPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2.port())
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .forDevice(CP1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .build();
+
+        List<NetworkResource> resources = ImmutableList.of(CP1.deviceId());
+
+        FlowRuleIntent intent = new FlowRuleIntent(APP_ID,
+                                                   KEY1,
+                                                   ImmutableList.of(flowRule),
+                                                   resources,
+                                                   PathIntent.ProtectionType.PRIMARY,
+                                                   RG1);
+
+        List<Intent> flowRuleIntents = Lists.newArrayList();
+        flowRuleIntents.add(intent);
+
+        return flowRuleIntents;
+    }
+
+    /**
+     * The FlowRuleService for test; always success for any flow rule operations.
+     */
+    class TestFlowRuleService extends FlowRuleServiceAdapter {
+
+        Set<FlowRule> flowRulesAdd = Sets.newHashSet();
+        Set<FlowRule> flowRulesRemove = Sets.newHashSet();
+
+        public void record(FlowRuleOperations ops) {
+            flowRulesAdd.clear();
+            flowRulesRemove.clear();
+            ops.stages().forEach(stage -> {
+                stage.forEach(op -> {
+                    switch (op.type()) {
+                        case ADD:
+                            flowRulesAdd.add(op.rule());
+                            break;
+                        case REMOVE:
+                            flowRulesRemove.add(op.rule());
+                            break;
+                        default:
+                            break;
+                    }
+                });
+            });
+        }
+
+        @Override
+        public void apply(FlowRuleOperations ops) {
+            record(ops);
+            ops.callback().onSuccess(ops);
+        }
+    }
+
+    /**
+     * The FlowRuleService for test; always failed for any flow rule operations.
+     */
+    class TestFailedFlowRuleService extends TestFlowRuleService {
+        @Override
+        public void apply(FlowRuleOperations ops) {
+            record(ops);
+            ops.callback().onError(ops);
+        }
+    }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstallerTest.java
new file mode 100644
index 0000000..53851da
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/ProtectionEndpointIntentInstallerTest.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2017-present 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.installer;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
+import org.onosproject.net.behaviour.protection.TransportEndpointDescription;
+import org.onosproject.net.config.BaseConfig;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.NetworkConfigServiceAdapter;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.ProtectionEndpointIntent;
+import org.onosproject.store.service.WallClockTimestamp;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for protection endpoint Intent installer.
+ */
+public class ProtectionEndpointIntentInstallerTest extends AbstractIntentInstallerTest {
+    private static final String FINGERPRINT = "Test fingerprint";
+    protected ProtectionEndpointIntentInstaller installer;
+    protected NetworkConfigService networkConfigService;
+    private static TestServiceDirectory directory;
+    private static ServiceDirectory original;
+
+    @BeforeClass
+    public static void setUpClass() throws TestUtils.TestUtilsException {
+        directory = new TestServiceDirectory();
+
+        CodecManager codecService = new CodecManager();
+        codecService.activate();
+        directory.add(CodecService.class, codecService);
+
+        // replace service directory used by BaseConfig
+        original = TestUtils.getField(BaseConfig.class, "services");
+        TestUtils.setField(BaseConfig.class, "services", directory);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws TestUtils.TestUtilsException {
+        TestUtils.setField(BaseConfig.class, "services", original);
+    }
+
+    @Before
+    public void setup() {
+        super.setup();
+        networkConfigService = new TestNetworkConfigService();
+        installer = new ProtectionEndpointIntentInstaller();
+        installer.networkConfigService = networkConfigService;
+        installer.intentExtensionService = intentExtensionService;
+        installer.intentInstallCoordinator = intentInstallCoordinator;
+        installer.trackerService = trackerService;
+
+        installer.activate();
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        installer.deactivated();
+    }
+
+    /**
+     * Installs protection endpoint Intents.
+     * framework.
+     */
+    @Test
+    public void testInstallIntents() {
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createProtectionIntents(CP2);
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Uninstalls protection endpoint Intents.
+     * framework.
+     */
+    @Test
+    public void testUninstallIntents() {
+        List<Intent> intentsToUninstall = createProtectionIntents(CP2);
+        List<Intent> intentsToInstall = Lists.newArrayList();
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLING,
+                                                new WallClockTimestamp());
+        IntentData toInstall = null;
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Test both uninstall and install protection endpoint Intents.
+     * framework.
+     */
+    @Test
+    public void testUninstallAndInstallIntents() {
+        List<Intent> intentsToUninstall = createProtectionIntents(CP2);
+        List<Intent> intentsToInstall = createProtectionIntents(CP3);
+        IntentData toUninstall = new IntentData(createP2PIntent(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToInstall);
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.successContext, operationContext);
+    }
+
+    /**
+     * Nothing to uninstall or install.
+     */
+    @Test
+    public void testNoAnyIntentToApply() {
+        IntentData toInstall = null;
+        IntentData toUninstall = null;
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext<>(ImmutableList.of(), ImmutableList.of(), context);
+        installer.apply(operationContext);
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
+     * Test if installation failed.
+     * framework.
+     */
+    @Test
+    public void testInstallFailed() {
+        networkConfigService = new TestFailedNetworkConfigService();
+        installer.networkConfigService = networkConfigService;
+        List<Intent> intentsToUninstall = Lists.newArrayList();
+        List<Intent> intentsToInstall = createProtectionIntents(CP2);
+        IntentData toUninstall = null;
+        IntentData toInstall = new IntentData(createP2PIntent(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentOperationContext<ProtectionEndpointIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+        installer.apply(operationContext);
+        assertEquals(intentInstallCoordinator.failedContext, operationContext);
+    }
+
+    /**
+     * Creates protection endpoint Intents by givent output point.
+     *
+     * @param output the output point
+     * @return the protection endpoint Intents
+     */
+    private List<Intent> createProtectionIntents(ConnectPoint output) {
+        FilteredConnectPoint filteredOutput = new FilteredConnectPoint(output);
+        TransportEndpointDescription path = TransportEndpointDescription.builder()
+                .withOutput(filteredOutput)
+                .withEnabled(true).build();
+
+        List<TransportEndpointDescription> paths = ImmutableList.of(path);
+        ProtectedTransportEndpointDescription description =
+                ProtectedTransportEndpointDescription.of(paths, CP2.deviceId(), FINGERPRINT);
+        ProtectionEndpointIntent intent = ProtectionEndpointIntent.builder()
+                .appId(APP_ID)
+                .description(description)
+                .deviceId(CP1.deviceId())
+                .key(KEY1)
+                .resourceGroup(RG1)
+                .build();
+
+        return ImmutableList.of(intent);
+    }
+
+    class TestNetworkConfigService extends NetworkConfigServiceAdapter {
+        protected Set<NetworkConfigListener> listeners = Sets.newHashSet();
+
+        @Override
+        public void addListener(NetworkConfigListener listener) {
+            listeners.add(listener);
+        }
+
+        @Override
+        public void removeListener(NetworkConfigListener listener) {
+            listeners.remove(listener);
+        }
+
+        @Override
+        public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+            return null;
+        }
+
+        @Override
+        public <S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_REMOVED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+        }
+    }
+
+    /**
+     * Test network config service; will send wrong events to listeners.
+     */
+    class TestFailedNetworkConfigService extends TestNetworkConfigService {
+
+        @Override
+        public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_REMOVED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+            return null;
+        }
+
+        @Override
+        public <S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass) {
+            NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED,
+                                                              subject,
+                                                              configClass);
+            CompletableFuture.runAsync(() -> {
+                listeners.forEach(listener -> listener.event(event));
+            });
+        }
+    }
+}
diff --git a/core/store/dist/src/test/java/org/onosproject/store/intent/impl/IntentStoreAdapter.java b/core/store/dist/src/test/java/org/onosproject/store/intent/impl/IntentStoreAdapter.java
new file mode 100644
index 0000000..131a1ed
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onosproject/store/intent/impl/IntentStoreAdapter.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2017-present 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.store.intent.impl;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentStore;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.Key;
+
+import java.util.List;
+
+/**
+ * Adapter for IntentStore.
+ */
+public class IntentStoreAdapter implements IntentStore {
+    @Override
+    public long getIntentCount() {
+        return 0;
+    }
+
+    @Override
+    public Iterable<Intent> getIntents() {
+        return null;
+    }
+
+    @Override
+    public Iterable<IntentData> getIntentData(boolean localOnly, long olderThan) {
+        return null;
+    }
+
+    @Override
+    public IntentState getIntentState(Key intentKey) {
+        return null;
+    }
+
+    @Override
+    public List<Intent> getInstallableIntents(Key intentKey) {
+        return null;
+    }
+
+    @Override
+    public void write(IntentData newData) {
+
+    }
+
+    @Override
+    public void batchWrite(Iterable<IntentData> updates) {
+
+    }
+
+    @Override
+    public Intent getIntent(Key key) {
+        return null;
+    }
+
+    @Override
+    public IntentData getIntentData(Key key) {
+        return null;
+    }
+
+    @Override
+    public void addPending(IntentData intent) {
+
+    }
+
+    @Override
+    public boolean isMaster(Key intentKey) {
+        return false;
+    }
+
+    @Override
+    public Iterable<Intent> getPending() {
+        return null;
+    }
+
+    @Override
+    public Iterable<IntentData> getPendingData() {
+        return null;
+    }
+
+    @Override
+    public IntentData getPendingData(Key intentKey) {
+        return null;
+    }
+
+    @Override
+    public Iterable<IntentData> getPendingData(boolean localOnly, long olderThan) {
+        return null;
+    }
+
+    @Override
+    public void setDelegate(IntentStoreDelegate delegate) {
+
+    }
+
+    @Override
+    public void unsetDelegate(IntentStoreDelegate delegate) {
+
+    }
+
+    @Override
+    public boolean hasDelegate() {
+        return false;
+    }
+}