IntentMonitorAndReroute initial contribution

Change-Id: I88616235b1e8ae28894da75b3fc8d46cb209dac5
diff --git a/apps/imr/BUCK b/apps/imr/BUCK
new file mode 100644
index 0000000..ffd56b2
--- /dev/null
+++ b/apps/imr/BUCK
@@ -0,0 +1,12 @@
+BUNDLES = [
+    '//apps/imr/api:onos-apps-imr-api',
+    '//apps/imr/app:onos-apps-imr-app',
+]
+
+onos_app (
+    title = 'Intent Monitoring and Rerouting',
+    category = 'Traffic Engineering',
+    url = 'http://onosproject.org', # link alla wiki su wiki.onosproject.org
+    description = 'Intent Monitoring and Rerouting application.',
+    included_bundles = BUNDLES,
+)
diff --git a/apps/imr/api/BUCK b/apps/imr/api/BUCK
new file mode 100644
index 0000000..a6a3885
--- /dev/null
+++ b/apps/imr/api/BUCK
@@ -0,0 +1,22 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//lib:JACKSON',
+    '//lib:javax.ws.rs-api',
+    '//utils/rest:onlab-rest',
+    '//lib:jersey-server',
+    '//core/store/serializers:onos-core-serializers',
+    '//apps/imr/app:onos-apps-imr-app',
+    '//cli:onos-cli',
+    '//lib:org.apache.karaf.shell.console',
+]
+
+
+osgi_jar_with_tests (
+    deps = COMPILE_DEPS,
+    web_context = '/onos/v1/imr',
+    api_title = 'IMR REST API',
+    api_version = '1.0',
+    api_description = 'REST API for IMR Application',
+    api_package = 'org.onosproject.imr.rest',
+)
+
diff --git a/apps/imr/api/pom.xml b/apps/imr/api/pom.xml
new file mode 100644
index 0000000..590342a
--- /dev/null
+++ b/apps/imr/api/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-imr</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>onos-app-imr-api</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Intent Monitoring and Rerouting application REST</description>
+
+    <properties>
+        <web.context>/onos/v1/imr</web.context>
+        <api.version>1.0.0</api.version>
+        <api.title>IMR Server REST API</api.title>
+        <api.description>
+            APIs for interacting with the IMR application.
+        </api.description>
+        <api.package>org.onosproject.imr.rest</api.package>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-imr-app</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <version>2.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationIdImrCompleter.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationIdImrCompleter.java
new file mode 100644
index 0000000..010c133
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationIdImrCompleter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.intent.IntentService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Application ID completer.
+ */
+public class ApplicationIdImrCompleter implements Completer {
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+
+        // Fetch our service and feed it's offerings to the string completer
+        IntentService service = AbstractShellCommand.get(IntentService.class);
+        SortedSet<String> strings = delegate.getStrings();
+
+        service.getIntents()
+                .forEach(intent ->
+                                 strings.add(Short.toString(intent.appId().id())));
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationNameImrCompleter.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationNameImrCompleter.java
new file mode 100644
index 0000000..d5f7bd4
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/ApplicationNameImrCompleter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.intent.IntentService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Application name completer.
+ */
+public class ApplicationNameImrCompleter implements Completer {
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+
+        // Fetch our service and feed it's offerings to the string completer
+        IntentService service = AbstractShellCommand.get(IntentService.class);
+        SortedSet<String> strings = delegate.getStrings();
+
+        service.getIntents()
+                .forEach(intent ->
+                                 strings.add(intent.appId().name()));
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/IntentKeyImrCompleter.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/IntentKeyImrCompleter.java
new file mode 100644
index 0000000..8b5b150
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/IntentKeyImrCompleter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+
+import java.util.List;
+import java.util.SortedSet;
+
+
+/**
+ * Intent Key completer for IMR cli command.
+ */
+public class IntentKeyImrCompleter implements Completer {
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+
+        // Fetch our service and feed it's offerings to the string completer
+        IntentService service = AbstractShellCommand.get(IntentService.class);
+        SortedSet<String> strings = delegate.getStrings();
+        service.getIntents().forEach(intent -> {
+            if (intent instanceof LinkCollectionIntent
+                    || intent instanceof PointToPointIntent) {
+                strings.add(intent.key().toString());
+            }
+        });
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
+
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/StartMonitorCommand.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StartMonitorCommand.java
new file mode 100644
index 0000000..dfba41c
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StartMonitorCommand.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.imr.IntentMonitorAndRerouteService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+
+/**
+ * Starts monitoring of an intent submitting its key to the IMR service.
+ */
+@Command(scope = "imr", name = "startmon",
+        description = "Submit an intent to the IMR application to start monitoring")
+public class StartMonitorCommand extends AbstractShellCommand {
+
+    @Option(name = "-l", aliases = "--longkey", description = "Treat intentKey as LongKey",
+            required = false, multiValued = false)
+    private boolean treatAsLongKey = false;
+
+    @Argument(index = 0, name = "applicationId",
+            description = "Application ID that submitted the intent",
+            required = true)
+    private Short appId;
+
+    @Argument(index = 1, name = "applicationName",
+            description = "Application Name that submitted the intent",
+            required = true)
+    private String appName;
+
+    @Argument(index = 2, name = "intentKey",
+            description = "String representation of the key of the intent",
+            required = false)
+    private String key;
+
+    private IntentMonitorAndRerouteService imrService;
+    private IntentService intentService;
+
+    @Override
+    protected void execute() {
+        imrService = get(IntentMonitorAndRerouteService.class);
+        intentService = get(IntentService.class);
+
+        if (appId != null && appName != null) {
+            if (key != null) {
+                /*
+                Intent key might be a StringKey or a LongKey, but in any case is
+                provided via CLI as a string. To solve only ambiguity we check if
+                "--longkey" CLI parameter has been set.
+                 */
+                if (treatAsLongKey) {
+                    try {
+                        Key intentKeyLong = Key.of(Integer.decode(key), new DefaultApplicationId(appId, appName));
+                        for (Intent intent : intentService.getIntents()) {
+                            if (intent.key().equals(intentKeyLong)) {
+                                imrService.startMonitorIntent(intentKeyLong);
+                                print("Started monitoring of intent with LongKey %s", intentKeyLong);
+                                return;
+                            }
+                        }
+                        imrService.startMonitorIntent(intentKeyLong);
+                        print("Started monitoring of intent with LongKey %s, even if not yet submitted", intentKeyLong);
+                    } catch (NumberFormatException nfe) {
+                        print("\"%s\" is not a valid LongKey", key);
+                    }
+                } else {
+                    Key intentKeyString = Key.of(key, new DefaultApplicationId(appId, appName));
+                    for (Intent intent : intentService.getIntents()) {
+                        if (intent.key().equals(intentKeyString)) {
+                            imrService.startMonitorIntent(intentKeyString);
+                            print("Started monitoring of intent with StringKey %s", intentKeyString);
+                            return;
+                        }
+                    }
+                    imrService.startMonitorIntent(intentKeyString);
+                    print("Started monitoring of intent with StringKey %s, even if not yet submitted", intentKeyString);
+                }
+            } else {
+                intentService.getIntents().forEach(i -> {
+                    if (i.appId().equals(new DefaultApplicationId(appId, appName))) {
+                        imrService.startMonitorIntent(i.key());
+                        print("Started monitoring of intent with Key %s", i.key());
+                    }
+                });
+            }
+        }
+    }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/StopMonitorCommand.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StopMonitorCommand.java
new file mode 100644
index 0000000..933b4be
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/StopMonitorCommand.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.imr.IntentMonitorAndRerouteService;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+
+/**
+ * Stops monitoring of an intent by the IMR service.
+ */
+@Command(scope = "imr", name = "stopmon",
+        description = "Stop monitoring and intent already submitted to the IMR")
+public class StopMonitorCommand extends AbstractShellCommand {
+
+    @Option(name = "-l", aliases = "--longkey", description = "Treat intentKey as LongKey",
+            required = false, multiValued = false)
+    private boolean treatAsLongKey = false;
+
+    @Argument(index = 0, name = "applicationId",
+            description = "Application ID that submitted the intent",
+            required = true)
+    private Short appId = null;
+
+    @Argument(index = 1, name = "applicationName",
+            description = "Application Name that submitted the intent",
+            required = true)
+    private String appName = null;
+
+    @Argument(index = 2, name = "intentKey",
+            description = "String representation of the key of the intent",
+            required = false)
+    private String key = null;
+
+    private IntentMonitorAndRerouteService imrService;
+    private IntentService intentService;
+
+    @Override
+    protected void execute() {
+        imrService = get(IntentMonitorAndRerouteService.class);
+        intentService = get(IntentService.class);
+
+        if (appId != null && appName != null) {
+            if (key != null) {
+                /*
+                Intent key might be a StringKey or a LongKey, but in any case is
+                provided via CLI as a string. To solve only ambiguity we check if
+                "--longkey" CLI parameter has been set.
+                 */
+                if (treatAsLongKey) {
+                    try {
+                        Key intentKeyLong = Key.of(Integer.decode(key), new DefaultApplicationId(appId, appName));
+                        if (imrService.stopMonitorIntent(intentKeyLong)) {
+                            print("Stopped monitoring of intent with LongKey %s", intentKeyLong);
+                            return;
+                        }
+                    } catch (NumberFormatException nfe) {
+                        print("\"%s\" is not a valid LongKey", key);
+                        return;
+                    }
+                } else {
+                    Key intentKeyString = Key.of(key, new DefaultApplicationId(appId, appName));
+                    if (imrService.stopMonitorIntent(intentKeyString)) {
+                        print("Stopped monitoring of intent with StringKey %s", intentKeyString);
+                        return;
+                    }
+                }
+
+                //No intent found in IMR
+                print("No monitored intent with key %s found", key);
+            } else {
+                intentService.getIntents().forEach(i -> {
+                    if (i.appId().equals(new DefaultApplicationId(appId, appName))) {
+                        imrService.stopMonitorIntent(i.key());
+                        print("Stopped monitoring of intent with key %s", i.key());
+                    }
+                });
+            }
+        }
+
+    }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/cli/package-info.java b/apps/imr/api/src/main/java/org/onosproject/imr/cli/package-info.java
new file mode 100644
index 0000000..4ade153
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Intent Monitor and Reroute application cli command.
+ */
+package org.onosproject.imr.cli;
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebApplication.java b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebApplication.java
new file mode 100644
index 0000000..d09e32a
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.rest;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+/**
+ * Intent Monitor and Reroute web application.
+ */
+public class ImrWebApplication extends AbstractWebApplication {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return getClasses(ImrWebResource.class);
+    }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebResource.java b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebResource.java
new file mode 100644
index 0000000..8e717d0
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/rest/ImrWebResource.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.rest;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.imr.IntentMonitorAndRerouteService;
+import org.onosproject.imr.data.Route;
+import org.onosproject.imr.data.RoutingConfigurations;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.intent.Key;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Intent Monitor and Reroute REST API.
+ */
+@Path("imr")
+public class ImrWebResource extends AbstractWebResource {
+
+    public static final String ROOT_FIELD_STATISTICS_NAME = "statistics";
+    public static final String ROOT_FIELD_MONITORED_INTENTS = "response";
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private IntentMonitorAndRerouteService imrService;
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    /**
+     * Get the statistics of the monitored intents.
+     * Shows for each intent all the flow entries.
+     *
+     * @onos.rsModel intentStatsGet
+     * @return 200 OK
+     */
+    @GET
+    @Path("intentStats")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getIntentsStats() {
+        ObjectNode root = mapper.createObjectNode();
+        imrService = get(IntentMonitorAndRerouteService.class);
+        root.putArray(ROOT_FIELD_STATISTICS_NAME).addAll(
+                getJsonNodesIntentStats(imrService.getStats()));
+        return ok(root).build();
+    }
+
+    /**
+     * Get the statistics of the monitored intent of a specific application
+     * Shows for each intent all the flow entries.
+     *
+     * @onos.rsModel intentStatsGet
+     * @param id Application ID
+     * @param name Application Name
+     * @return 200 OK
+     */
+    @GET
+    @Path("intentStats/{id}/{name}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getIntentsStats(@PathParam("id") short id, @PathParam("name") String name) {
+        ObjectNode root = mapper.createObjectNode();
+        imrService = get(IntentMonitorAndRerouteService.class);
+        ApplicationId appId = new DefaultApplicationId(id, name);
+        root.putArray(ROOT_FIELD_STATISTICS_NAME).addAll(
+                getJsonNodesIntentStats(imrService.getStats(appId)));
+        return ok(root).build();
+    }
+
+    /**
+     * Get the statistics of a specific monitored intent.
+     * Shows all the flow entries of the specific intent
+     *
+     * @onos.rsModel intentStatsGet
+     * @param id Application ID
+     * @param name Application Name
+     * @param intentK Intent Key
+     * @return 200 OK
+     */
+    @GET
+    @Path("intentStats/{id}/{name}/{intentKey}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getIntentsStats(@PathParam("id") short id,
+                                    @PathParam("name") String name,
+                                    @PathParam("intentKey") String intentK) {
+        ObjectNode root = mapper.createObjectNode();
+        imrService = get(IntentMonitorAndRerouteService.class);
+        ApplicationId appId = new DefaultApplicationId(id, name);
+        Key intentKey = Key.of(intentK, appId);
+        root.putArray("statistics").addAll(
+                getJsonNodesIntentStats(imrService.getStats(appId, intentKey)));
+        return ok(root).build();
+    }
+
+    /**
+     * Build the Json Nodes from the intent stats retrieved from {@link IntentMonitorAndRerouteService}.
+     *
+     * @param mapKeyToStats Intent statistics
+     * @return {@link ArrayNode} built from the statistics
+     */
+    private ArrayNode getJsonNodesIntentStats(Map<ApplicationId, Map<Key, List<FlowEntry>>> mapKeyToStats) {
+        final ArrayNode rootArrayNode = mapper.createArrayNode();
+        mapKeyToStats.forEach((appId, mapIntentKeyStats) -> {
+            ObjectNode appObjNode = codec(ApplicationId.class).encode(appId, this);
+            ArrayNode intentArrayNode = appObjNode.putArray("intents");
+            mapIntentKeyStats.forEach((intentKey, lstStats) -> {
+                ObjectNode intentKeyObjNode = mapper.createObjectNode();
+                ArrayNode statsArrayNode = intentKeyObjNode.putArray(intentKey.toString());
+                lstStats.forEach(stat -> {
+                    statsArrayNode.add(codec(FlowEntry.class).encode(stat, this));
+                });
+
+                intentArrayNode.add(intentKeyObjNode);
+            });
+
+            rootArrayNode.add(appObjNode);
+        });
+        return rootArrayNode;
+    }
+
+    /**
+     * Get the list of monitored intents.
+     * Shows for each intent key the related end points (as inElements and OutElements).
+     *
+     * @onos.rsModel monitoredIntentsGet
+     * @return 200 OK
+     */
+    @GET
+    @Path("monitoredIntents")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getMonitoredIntents() {
+        imrService = get(IntentMonitorAndRerouteService.class);
+        ArrayNode jsonMonitoredIntents = getJsonMonitoredIntents(imrService.getMonitoredIntents());
+        ObjectNode result = mapper.createObjectNode();
+        result.putArray(ROOT_FIELD_MONITORED_INTENTS).addAll(jsonMonitoredIntents);
+        return ok(result).build();
+    }
+
+    /**
+     * Get the list of monitored intents of a specific application.
+     * Shows for each intent key the related end points (as inElements and OutElements).
+     *
+     * @onos.rsModel monitoredIntentsGet
+     * @param id Application ID
+     * @param name Application Name
+     * @return 200 OK
+     */
+    @GET
+    @Path("monitoredIntents/{id}/{name}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getMonitoredIntents(@PathParam("id") short id, @PathParam("name") String name) {
+        imrService = get(IntentMonitorAndRerouteService.class);
+        ApplicationId appId = new DefaultApplicationId(id, name);
+        ArrayNode jsonMonitoredIntents = getJsonMonitoredIntents(imrService.getMonitoredIntents(appId));
+        ObjectNode result = mapper.createObjectNode();
+        result.putArray(ROOT_FIELD_MONITORED_INTENTS).addAll(jsonMonitoredIntents);
+        return ok(result).build();
+    }
+
+    /**
+     * Build the JSON Node from the monitored intents retrieved from {@link IntentMonitorAndRerouteService}.
+     *
+     * @param monIntents Monitored intents structure.
+     * @return {@link ArrayNode} built from the monitored intents.
+     */
+    private ArrayNode getJsonMonitoredIntents(
+            Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> monIntents) {
+        final ArrayNode rootArrayNode = mapper.createArrayNode();
+        monIntents.forEach((appId, mapIntentKeyEndElements) -> {
+            ObjectNode appObjNode = codec(ApplicationId.class).encode(appId, this);
+            ArrayNode intentArrayNode = appObjNode.putArray("intents");
+            mapIntentKeyEndElements.forEach((intentKey, inOutElem) -> {
+                ObjectNode intentKeyObjNode = mapper.createObjectNode()
+                        .put("key", intentKey.toString());
+                ArrayNode inElements = intentKeyObjNode.putArray("inElements");
+                inOutElem.getLeft().forEach(elementId -> inElements.add(elementId.toString()));
+                ArrayNode outElements = intentKeyObjNode.putArray("outElements");
+                inOutElem.getRight().forEach(elementId -> outElements.add(elementId.toString()));
+                intentArrayNode.add(intentKeyObjNode);
+            });
+
+            rootArrayNode.add(appObjNode);
+        });
+        return rootArrayNode;
+    }
+
+
+    /**
+     * POST a list of routing configurations. For each intents a list of paths
+     * with relative weights is specified. The body of HTTP POST is a JSON
+     *
+     * @onos.rsModel reRouteIntentsPost
+     * @param stream JSON stream
+     * @return 200 OK if the routing configurations are applied correctly,
+     * otherwise 500 Internal Server Error
+     */
+    @POST
+    @Path("reRouteIntents")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response reRouteIntents(InputStream stream) {
+        imrService = get(IntentMonitorAndRerouteService.class);
+        ObjectNode result = mapper().createObjectNode();
+        StringBuilder resultString = new StringBuilder();
+
+        mapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        try {
+            RoutingConfigurations msg = mapper().readValue(stream, RoutingConfigurations.class);
+            for (Route routingConfiguration: msg.routingList()) {
+                String outcome;
+                try {
+                     if (!imrService.applyPath(routingConfiguration)) {
+                         outcome = "Application ID:" + routingConfiguration.appId()
+                                 + " or Intent Key:" + routingConfiguration.key()
+                                 + " not monitored!";
+                     } else {
+                         outcome = "OK";
+                     }
+                } catch (IllegalArgumentException | NullPointerException ex) {
+                    outcome = ex.getMessage();
+                }
+                if (!outcome.equals("OK")) {
+                    if (resultString.length() > 0) {
+                        resultString.append(" ");
+                    }
+                    resultString.append(outcome);
+                }
+            }
+            if (resultString.length() > 0) {
+                result.put("response", "addRouting() failed: ".concat(resultString.toString()));
+            } else {
+                result.put("response", "OK");
+            }
+            return ok(result).build();
+        } catch (Exception e) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).
+                    entity(e.toString())
+                    .build();
+        }
+    }
+}
diff --git a/apps/imr/api/src/main/java/org/onosproject/imr/rest/package-info.java b/apps/imr/api/src/main/java/org/onosproject/imr/rest/package-info.java
new file mode 100644
index 0000000..bdd5740
--- /dev/null
+++ b/apps/imr/api/src/main/java/org/onosproject/imr/rest/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Intent Monitor and Reroute application.
+ */
+package org.onosproject.imr.rest;
diff --git a/apps/imr/api/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/imr/api/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..fdddc88
--- /dev/null
+++ b/apps/imr/api/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,29 @@
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+
+        <command>
+            <action class="org.onosproject.imr.cli.StartMonitorCommand"/>
+            <completers>
+                <ref component-id="applicationIdCompleter"/>
+                <ref component-id="applicationNameCompleter"/>
+                <ref component-id="intentKeyImrCompleter"/>
+            </completers>
+        </command>
+
+        <command>
+            <action class="org.onosproject.imr.cli.StopMonitorCommand"/>
+            <completers>
+                <ref component-id="applicationIdCompleter"/>
+                <ref component-id="applicationNameCompleter"/>
+                <ref component-id="intentKeyImrCompleter"/>
+            </completers>
+        </command>
+
+    </command-bundle>
+
+    <bean id="applicationIdCompleter" class="org.onosproject.imr.cli.ApplicationIdImrCompleter"/>
+    <bean id="applicationNameCompleter" class="org.onosproject.imr.cli.ApplicationNameImrCompleter"/>
+    <bean id="intentKeyImrCompleter" class="org.onosproject.imr.cli.IntentKeyImrCompleter"/>
+
+</blueprint>
diff --git a/apps/imr/api/src/main/resources/definitions/intentStatsGet.json b/apps/imr/api/src/main/resources/definitions/intentStatsGet.json
new file mode 100644
index 0000000..5e60ccb
--- /dev/null
+++ b/apps/imr/api/src/main/resources/definitions/intentStatsGet.json
@@ -0,0 +1,96 @@
+{
+  "statistics": [
+    {
+      "id": 71,
+      "name": "org.onosproject.ifwd",
+      "intents": [
+        {
+          "00:00:00:00:00:08/None00:00:00:00:00:01/None": [
+            {
+              "id": "41658299096955970",
+              "tableId": 0,
+              "appId": "org.onosproject.net.intent",
+              "groupId": 0,
+              "priority": 100,
+              "timeout": 0,
+              "isPermanent": true,
+              "deviceId": "of:000000000000000c",
+              "state": "ADDED",
+              "life": 12,
+              "packets": 12,
+              "bytes": 1176,
+              "liveType": "UNKNOWN",
+              "lastSeen": 1512041486127,
+              "treatment": {
+                "instructions": [
+                  {
+                    "type": "OUTPUT",
+                    "port": "2"
+                  }
+                ],
+                "deferred": []
+              },
+              "selector": {
+                "criteria": [
+                  {
+                    "type": "IN_PORT",
+                    "port": 4
+                  },
+                  {
+                    "type": "ETH_DST",
+                    "mac": "00:00:00:00:00:01"
+                  },
+                  {
+                    "type": "ETH_SRC",
+                    "mac": "00:00:00:00:00:08"
+                  }
+                ]
+              }
+            },
+            {
+              "id": "41658299401587474",
+              "tableId": 0,
+              "appId": "org.onosproject.net.intent",
+              "groupId": 0,
+              "priority": 100,
+              "timeout": 0,
+              "isPermanent": true,
+              "deviceId": "of:0000000000000002",
+              "state": "ADDED",
+              "life": 12,
+              "packets": 12,
+              "bytes": 1176,
+              "liveType": "UNKNOWN",
+              "lastSeen": 1512041486122,
+              "treatment": {
+                "instructions": [
+                  {
+                    "type": "OUTPUT",
+                    "port": "2"
+                  }
+                ],
+                "deferred": []
+              },
+              "selector": {
+                "criteria": [
+                  {
+                    "type": "IN_PORT",
+                    "port": 3
+                  },
+                  {
+                    "type": "ETH_DST",
+                    "mac": "00:00:00:00:00:01"
+                  },
+                  {
+                    "type": "ETH_SRC",
+                    "mac": "00:00:00:00:00:08"
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/resources/definitions/monitoredIntentsGet.json b/apps/imr/api/src/main/resources/definitions/monitoredIntentsGet.json
new file mode 100644
index 0000000..c577adc
--- /dev/null
+++ b/apps/imr/api/src/main/resources/definitions/monitoredIntentsGet.json
@@ -0,0 +1,28 @@
+{
+  "response": [
+    {
+      "id": 71,
+      "name": "org.onosproject.ifwd",
+      "intents": [
+        {
+          "key": "00:00:00:00:00:08/None00:00:00:00:00:01/None",
+          "inElements": [
+            "00:00:00:00:00:08/None"
+          ],
+          "outElements": [
+            "00:00:00:00:00:01/None"
+          ]
+        },
+        {
+          "key": "00:00:00:00:00:01/None00:00:00:00:00:08/None",
+          "inElements": [
+            "00:00:00:00:00:01/None"
+          ],
+          "outElements": [
+            "00:00:00:00:00:08/None"
+          ]
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/resources/definitions/reRouteIntentsPost.json b/apps/imr/api/src/main/resources/definitions/reRouteIntentsPost.json
new file mode 100644
index 0000000..e6c088b
--- /dev/null
+++ b/apps/imr/api/src/main/resources/definitions/reRouteIntentsPost.json
@@ -0,0 +1,44 @@
+{
+  "routingList": [
+    {
+      "key": "00:00:00:00:00:08/None00:00:00:00:00:01/None",
+      "appId": {
+        "id" : 71,
+        "name" : "org.onosproject.ifwd"
+      },
+      "paths": [
+        {
+          "path":
+          ["00:00:00:00:00:08/None",
+            "of:000000000000000c",
+            "of:0000000000000001",
+            "of:000000000000000d",
+            "of:0000000000000002",
+            "of:000000000000000b",
+            "00:00:00:00:00:01/None"],
+          "weight": 1.0
+        }
+      ]
+    },
+    {
+      "key": "00:00:00:00:00:01/None00:00:00:00:00:08/None",
+      "appId": {
+        "id" : 71,
+        "name" : "org.onosproject.ifwd"
+      },
+      "paths": [
+        {
+          "path":
+          ["00:00:00:00:00:01/None",
+            "of:000000000000000b",
+            "of:0000000000000002",
+            "of:000000000000000e",
+            "of:0000000000000001",
+            "of:000000000000000c",
+            "00:00:00:00:00:01/None"],
+          "weight": 1.0
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apps/imr/api/src/main/webapp/WEB-INF/web.xml b/apps/imr/api/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ce093c0
--- /dev/null
+++ b/apps/imr/api/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="ONOS" version="2.5">
+    <display-name>Intent Monitor and Reroute REST API</display-name>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Secured</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>admin</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>karaf</realm-name>
+    </login-config>
+
+    <servlet>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.onosproject.imr.rest.ImrWebApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/apps/imr/app/BUCK b/apps/imr/app/BUCK
new file mode 100644
index 0000000..4f39ad8
--- /dev/null
+++ b/apps/imr/app/BUCK
@@ -0,0 +1,17 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//core/store/dist:onos-core-dist',
+    '//core/store/serializers:onos-core-serializers',
+    '//incubator/api:onos-incubator-api',
+    '//lib:KRYO',
+    '//lib:JACKSON',
+]
+
+TEST_DEPS = [
+    '//lib:TEST_ADAPTERS',
+]
+
+osgi_jar_with_tests (
+    deps = COMPILE_DEPS,
+    test_deps = TEST_DEPS,
+)
diff --git a/apps/imr/app/pom.xml b/apps/imr/app/pom.xml
new file mode 100644
index 0000000..b66ebc6
--- /dev/null
+++ b/apps/imr/app/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>onos-app-imr-app</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Intent Monitoring and Rerouting application</description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-imr-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-net</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteManager.java b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteManager.java
new file mode 100644
index 0000000..3ab49aa
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteManager.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr;
+
+import com.google.common.collect.ImmutableSet;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.imr.data.Path;
+import org.onosproject.imr.data.Route;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.statistic.FlowStatisticStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.DistributedSet;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Manager of Intent Monitor and Reroute.
+ */
+@Component(immediate = true)
+@Service
+public class IntentMonitorAndRerouteManager implements IntentMonitorAndRerouteService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private ConsistentMap<ApplicationId, Map<Key, ConnectivityIntent>> monitoredIntentsDistr;
+    private Map<ApplicationId, Map<Key, ConnectivityIntent>> monitoredIntents;
+
+    private DistributedSet<Key> toBeMonitoredIntents;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentService intentService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkService linkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowStatisticStore statsStore;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private InternalIntentListener intentListener = new InternalIntentListener();
+
+    private InternalFlowRuleListener flowRuleListener = new InternalFlowRuleListener();
+
+    @Activate
+    protected void activate() {
+        intentService.addListener(intentListener);
+        flowRuleService.addListener(flowRuleListener);
+
+        monitoredIntentsDistr = storageService
+                .<ApplicationId, Map<Key, ConnectivityIntent>>consistentMapBuilder()
+                .withSerializer(Serializer.using(KryoNamespaces.API))
+                .withName("IMR-monitoredIntents")
+                .build();
+        monitoredIntents = monitoredIntentsDistr.asJavaMap();
+
+        toBeMonitoredIntents = storageService.<Key>setBuilder()
+                .withSerializer(Serializer.using(
+                        new KryoNamespace.Builder()
+                                .register(KryoNamespaces.API)
+                                .register(Key.class)
+                                .build()))
+                .withName("IMR-toMonitorIntents")
+                .build()
+                .asDistributedSet();
+        log.info("IntentMonitorAndReroute activated");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        intentService.removeListener(intentListener);
+        flowRuleService.removeListener(flowRuleListener);
+        monitoredIntents
+                .forEach(((applicationId, keyConnectivityIntentMap) ->
+                        keyConnectivityIntentMap.keySet()
+                                .forEach(this::removeIntent)));
+        log.info("IntentMonitorAndReroute deactivated");
+    }
+
+
+    private synchronized void storeMonitoredIntent(ConnectivityIntent intent) {
+        log.debug("Store Monitored Intent {}", intent.key());
+        Map<Key, ConnectivityIntent> temp = monitoredIntents.getOrDefault(intent.appId(), new ConcurrentHashMap<>());
+        temp.put(intent.key(), intent);
+        monitoredIntents.put(intent.appId(), temp);
+    }
+
+    @Override
+    public synchronized boolean startMonitorIntent(Key intentKey) {
+        checkNotNull(intentKey, "Intent Key must not be null");
+        log.debug("Start Monitor Intent: {}", intentKey.toString());
+        toBeMonitoredIntents.add(intentKey);
+
+        //Check if the requested intent is already present in the intent manager
+        Intent installedIntent = intentService.getIntent(intentKey);
+        if (!allowedIntent(installedIntent)) {
+            return false;
+        }
+        //Check if the intent that is present in the intent subsystem is already installed
+        if (intentService.getIntentState(intentKey) == IntentState.INSTALLED) {
+            storeMonitoredIntent((ConnectivityIntent) installedIntent);
+        }
+        return true;
+    }
+
+
+    /**
+     * Returns whether the intent can be monitored or not.
+     * @param intent The intent you want to check if it is allowed to be monitored.
+     * @return true if the intent's type is of one of the allowed types
+     * ({@link LinkCollectionIntent}, {@link PointToPointIntent}).
+     */
+    public boolean allowedIntent(Intent intent) {
+        return intent instanceof LinkCollectionIntent || intent instanceof PointToPointIntent;
+    }
+
+    @Override
+    public synchronized boolean stopMonitorIntent(Key intentKey) {
+        checkNotNull(intentKey, "Intent key must not be null");
+        log.debug("Stop Monitor Intent: ", intentKey.toString());
+        if (!toBeMonitoredIntents.contains(intentKey)) {
+            return false;
+        }
+        removeIntent(intentKey);
+        toBeMonitoredIntents.remove(intentKey);
+        return true;
+    }
+
+    /**
+     * Removes the intent from the internal structure.
+     * @param intentKey Key of the intent to be removed.
+     * @return true if the intent is found and removed, false otherwise.
+     */
+    private synchronized boolean removeIntent(Key intentKey) {
+        for (Map.Entry<ApplicationId, Map<Key, ConnectivityIntent>> appIntents
+                : monitoredIntents.entrySet()) {
+            if (appIntents.getValue().containsKey(intentKey)) {
+                appIntents.getValue().remove(intentKey);
+                //TODO: check if it works without reputting the map
+                flushIntentStatStore(intentKey);
+                monitoredIntents.put(appIntents.getKey(), appIntents.getValue());
+                if (appIntents.getValue().isEmpty()) {
+                    monitoredIntents.remove(appIntents.getKey());
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Flushes the statistics (from the statistics store) of an intent.
+     * @param intentKey Key of the intent which statistics has to be cleaned.
+     */
+    private synchronized void flushIntentStatStore(Key intentKey) {
+        checkNotNull(intentKey);
+        //Remove all the flow rule on the stats store related to the passed intentKey
+        intentService.getInstallableIntents(intentKey)
+                .stream()
+                .map(intent -> (FlowRuleIntent) intent)
+                .forEach(intent -> intent.flowRules()
+                        .forEach(flowRule -> statsStore.removeFlowStatistic(flowRule))
+                );
+    }
+
+
+    /**
+     * Generates a new {@Link LinkCollectionIntent} applying the new path.
+     * @param links List of links of the new path.
+     * @param intentKey Key of the intent you want to re-route.
+     * @param appId Application id that submits initially the intent.
+     * @return The new intent, if not possibile it will return the old intent already installed.
+     */
+    private ConnectivityIntent generateLinkCollectionIntent(
+            List<Link> links,
+            Key intentKey,
+            ApplicationId appId) {
+        checkNotNull(links);
+        checkNotNull(appId);
+
+        // Gets the oldIntent already installed
+        ConnectivityIntent oldIntent = monitoredIntents.get(appId).get(intentKey);
+
+        //Flush the statistics of the currently installed intent
+        flushIntentStatStore(intentKey);
+
+        //get the connect point of the old intent
+        // Left element of the Pair is the ingress, right one is the egress
+        Pair<Set<FilteredConnectPoint>, Set<FilteredConnectPoint>> cpPair = extractEndConnectPoints(oldIntent);
+        if (cpPair == null) {
+            return oldIntent;
+        }
+
+        // Now generate the new intent
+        LinkCollectionIntent newIntent = LinkCollectionIntent.builder()
+                .appId(oldIntent.appId())
+                .key(intentKey)
+                .selector(oldIntent.selector())
+                .filteredIngressPoints(ImmutableSet.copyOf(cpPair.getLeft()))
+                .filteredEgressPoints(ImmutableSet.copyOf(cpPair.getRight()))
+                .treatment(oldIntent.treatment())
+                .priority(oldIntent.priority())
+                .constraints(oldIntent.constraints())
+                .links(ImmutableSet.copyOf(links))
+                //TODO: is there a way to get from the old intent?
+                .applyTreatmentOnEgress(true)
+                .build();
+
+        return newIntent;
+    }
+
+    @Override
+    public boolean applyPath(Route route) {
+        checkNotNull(route, "Route to apply must be not null");
+        checkNotNull(route.appId(), "Application id must be not null");
+        checkNotNull(route.key(), "Intent key to apply must be not null");
+        checkNotNull(route.paths(), "New path must be not null");
+        checkArgument(route.paths().size() >= 1);
+
+        ApplicationId appId = route.appId();
+        Key key = route.key();
+
+        // check if the app and the intent key are monitored
+        if (!monitoredIntents.containsKey(appId)) {
+            return false;
+        }
+        if (!monitoredIntents.get(appId).containsKey(key)) {
+            return false;
+        }
+
+        // TODO: now we manage only the unsplittable routing
+        Path currentPath = route.paths()
+                .stream()
+                .max(Path::compareTo)
+                .get();
+
+        // Check if the last and first element of the path are HostId
+        // in this case remove them from the list
+        if (currentPath.path().get(0) instanceof HostId) {
+            currentPath.path().remove(0);
+        }
+        if (currentPath.path().get(currentPath.path().size() - 1) instanceof HostId) {
+            currentPath.path().remove(currentPath.path().size() - 1);
+        }
+
+        List<Link> links = createPathFromDeviceList(currentPath.path());
+
+        // Generate the new Link collection intent, if not possible it will return the old intent
+        ConnectivityIntent intent = generateLinkCollectionIntent(links, key, appId);
+        storeMonitoredIntent(intent);
+        intentService.submit(intent);
+        return true;
+    }
+
+    @Override
+    public Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats() {
+        //TODO: check if there is a better way to get the statistics
+        Map<ApplicationId, Map<Key, List<FlowEntry>>> currentStatistics = new HashMap<>();
+        monitoredIntents.forEach((appId, mapIntentKey) ->
+                                         currentStatistics.putAll(getStats(appId))
+        );
+        return currentStatistics;
+    }
+
+    @Override
+    public Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId) {
+        checkNotNull(appId);
+
+        //TODO: is there a better way to get statistics?
+        Map<ApplicationId, Map<Key, List<FlowEntry>>> currentStatistics = new HashMap<>();
+        currentStatistics.put(appId, new HashMap<>());
+        if (monitoredIntents.containsKey(appId)) {
+            Set<Key> keySet = monitoredIntents.get(appId).keySet();
+            for (Key intentKey : keySet) {
+
+                List<FlowEntry> flowEntries = getStats(intentKey);
+                currentStatistics.get(appId).put(intentKey, flowEntries);
+            }
+        }
+        return currentStatistics;
+    }
+
+    @Override
+    public Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId, Key intentKey) {
+        checkNotNull(appId);
+        checkNotNull(intentKey);
+        checkArgument(monitoredIntents.containsKey(appId));
+        checkArgument(monitoredIntents.get(appId).containsKey(intentKey));
+
+        Map<ApplicationId, Map<Key, List<FlowEntry>>> currentStatistics = new HashMap<>();
+        currentStatistics.put(appId, new HashMap<>());
+        List<FlowEntry> flowEntries = getStats(intentKey);
+        currentStatistics.get(appId).put(intentKey, flowEntries);
+        return currentStatistics;
+    }
+
+    /**
+     * Returns the list of flow entries of a particular intent.
+     * @param intentKey
+     * @return List of the flow entries of the specified intent,
+     * it contains all the statistics of that intent.
+     */
+    private List<FlowEntry> getStats(Key intentKey) {
+        List<FlowEntry> currentStatistics = new LinkedList<>();
+        intentService.getInstallableIntents(intentKey)
+                .forEach(intent -> ((FlowRuleIntent) intent).flowRules()
+                         .forEach(flowRule -> {
+                             ConnectPoint cp = buildConnectPoint(flowRule);
+                             currentStatistics.addAll(getStats(cp, flowRule));
+                         })
+                );
+        return currentStatistics;
+    }
+
+    /**
+     * Returns a list of flow entry related to the connect point and flow rule passed.
+     * @param cp ConnectPoint we want to retrieve the flow entry from.
+     * @param flowRule FlowRule.
+     * @return List of flow entries.
+     */
+    private List<FlowEntry> getStats(ConnectPoint cp, FlowRule flowRule) {
+        return statsStore.getCurrentFlowStatistic(cp)
+                .stream()
+                .filter(flowEntry -> flowEntry
+                        .id()
+                        .equals(flowRule.id()))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Returns a list of links starting from a list of devices.
+     * @param deviceList List of devices.
+     * @return A path in terms of list of links.
+     */
+    private List<Link> createPathFromDeviceList(List<ElementId> deviceList) {
+        List<Link> path = new ArrayList<>();
+        if (deviceList.size() == 1) {
+            return path;
+        }
+
+        // Left element represents the input and right the output
+        List<Pair<DeviceId, DeviceId>> devicePairs = IntStream.
+                range(0, deviceList.size() - 1)
+                .mapToObj(i -> Pair.of((DeviceId) deviceList.get(i), (DeviceId) deviceList.get(i + 1)))
+                .collect(Collectors.toList());
+
+        devicePairs.forEach(pair -> {
+            //TODO use GetPath pair by pair?
+            // The common Link between DevEgress and DevIngress is the intersection of their links
+            Set<Link> commonLinks = new HashSet<>(linkService.getDeviceEgressLinks(pair.getLeft()));
+            commonLinks.retainAll(linkService.getDeviceIngressLinks(pair.getRight()));
+            if (commonLinks.size() == 0) {
+                log.error("No link found between node {} and node {}!",
+                          pair.getLeft(), pair.getRight());
+            } else if (commonLinks.size() == 1) {
+                path.add(commonLinks.iterator().next());
+            } else {
+                //TODO select the one with more bandwidth?
+                log.warn("{} links found between node {} and node {}: taking the first one!",
+                         commonLinks.size(), pair.getLeft(), pair.getRight());
+                path.add(commonLinks.iterator().next());
+            }
+        });
+
+        return path;
+    }
+
+    public Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents() {
+        Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> currentMonitoredIntents
+                = new ConcurrentHashMap<>();
+        monitoredIntents.forEach((appId, appIntents) -> {
+            currentMonitoredIntents.put(appId, new ConcurrentHashMap<>());
+            appIntents.forEach((intentKey, intent) -> {
+                Pair<Set<ElementId>, Set<ElementId>> endPair = extractEndPoints(intent);
+                if (endPair != null) {
+                    currentMonitoredIntents.get(appId).put(intentKey, endPair);
+                }
+            });
+        });
+        return currentMonitoredIntents;
+    }
+
+    public Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents(
+            ApplicationId appId) {
+        Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> currentMonitoredIntents
+                = new ConcurrentHashMap<>();
+        currentMonitoredIntents.put(appId, new ConcurrentHashMap<>());
+        if (monitoredIntents.containsKey(appId)) {
+            monitoredIntents.get(appId).forEach((intentKey, intent) -> {
+                Pair<Set<ElementId>, Set<ElementId>> endPair = extractEndPoints(intent);
+                if (endPair != null) {
+                    currentMonitoredIntents.get(appId).put(intentKey, endPair);
+                }
+            });
+        }
+        return currentMonitoredIntents;
+    }
+
+    private Set<ElementId> connectedElements(Set<FilteredConnectPoint> cpSet) {
+        Set<ElementId> connectedElem = new HashSet<>();
+        cpSet.forEach(
+            fcp -> {
+                Set<Host> connectedHosts = hostService.getConnectedHosts(fcp.connectPoint());
+                if (connectedHosts.size() == 0) {
+                    // In this case the end point is an ELEMENT without host connected
+                    connectedElem.add(fcp.connectPoint().elementId());
+                } else {
+                    // In this case we can have a set of hosts connected to that endpoint
+                    connectedElem.addAll(connectedHosts.stream().map(Host::id)
+                                   .collect(Collectors.toSet()));
+                }
+            }
+        );
+        return connectedElem;
+    }
+
+    /**
+     * Extracts the endpoint from an intent.
+     * @param intent
+     * @return {@link Pair} containing in the Left element the set of input {@link ElementId},
+     * in the Right element the set of output {@link ElementId}.
+     */
+    private Pair<Set<ElementId>, Set<ElementId>> extractEndPoints(Intent intent) {
+        checkNotNull(intent, "intent must not be null");
+        Pair<Set<FilteredConnectPoint>, Set<FilteredConnectPoint>> cpPair;
+        cpPair = extractEndConnectPoints(intent);
+        if (cpPair == null) {
+            return null;
+        }
+        return Pair.of(connectedElements(cpPair.getLeft()), connectedElements(cpPair.getRight()));
+    }
+
+    /**
+     * Returns the end connect points of an intent.
+     * @param intent
+     * @return {@link Pair} containing in the Left element the input end connect points,
+     * in the Right element the output end connect points.
+     */
+    private Pair<Set<FilteredConnectPoint>, Set<FilteredConnectPoint>> extractEndConnectPoints(Intent intent) {
+        checkNotNull(intent, "intent must not be null");
+
+        Set<FilteredConnectPoint> inSet = new HashSet<>();
+        Set<FilteredConnectPoint> outSet = new HashSet<>();
+        if (intent instanceof PointToPointIntent) {
+            inSet.add(((PointToPointIntent) intent).filteredIngressPoint());
+            outSet.add(((PointToPointIntent) intent).filteredEgressPoint());
+        } else if (intent instanceof LinkCollectionIntent) {
+            inSet.addAll(((LinkCollectionIntent) intent).filteredIngressPoints());
+            outSet.addAll(((LinkCollectionIntent) intent).filteredEgressPoints());
+        }
+        return Pair.of(inSet, outSet);
+    }
+
+    /**
+     * Returns the connect point related to the output port of the rule.
+     * @param rule
+     * @return
+     */
+    private ConnectPoint buildConnectPoint(FlowRule rule) {
+        PortNumber port = getOutput(rule);
+
+        if (port == null) {
+            return null;
+        }
+        return new ConnectPoint(rule.deviceId(), port);
+    }
+
+    /**
+     * Returns the output port related to the rule.
+     * @param rule
+     * @return
+     */
+    private PortNumber getOutput(FlowRule rule) {
+        for (Instruction i : rule.treatment().allInstructions()) {
+            if (i.type() == Instruction.Type.OUTPUT) {
+                Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
+                return out.port();
+            }
+        }
+        return null;
+    }
+
+
+    private class InternalIntentListener implements IntentListener {
+
+        @Override
+        public void event(IntentEvent event) {
+            // It receives only events related to ConnectivityIntent to be monitored
+            Key intentKey = event.subject().key();
+            switch (event.type()) {
+                case INSTALLED:
+                    // When an intent is installed and it need to be monitored
+                    // it will pass from the "toBeMonitored" state to the "monitored" state
+                    log.info("Monitored intent INSTALLED");
+                    storeMonitoredIntent((ConnectivityIntent) event.subject());
+                    break;
+
+                case WITHDRAWN:
+                    // When an intent is withdrawn
+                    // it will go back from the "monitored" state to the "toBeMonitored"
+                    log.info("Monitored intent WITHDWRAWN");
+                    removeIntent(intentKey);
+                    break;
+
+                case FAILED:
+                    log.warn("FAILED event not handled");
+                    break;
+                default:
+                    log.warn("Unknown intent event");
+            }
+        }
+
+        @Override
+        public boolean isRelevant(IntentEvent event) {
+            /*
+             * Check if the Intent event is relevant.
+             * An intent event is relevant if it is of one of the allowed types
+             * and if it is one of the monitored ones.
+             */
+            Key intentKey = event.subject().key();
+            return allowedIntent(event.subject())
+                    && toBeMonitoredIntents.contains(intentKey);
+        }
+    }
+
+    private class InternalFlowRuleListener implements FlowRuleListener {
+        @Override
+        public void event(FlowRuleEvent event) {
+            FlowRule rule = event.subject();
+            switch (event.type()) {
+                case RULE_ADDED:
+                case RULE_UPDATED:
+                    // In case of rule update, flow statistics are updated
+                    if (rule instanceof FlowEntry) {
+                        statsStore.updateFlowStatistic((FlowEntry) rule);
+                    }
+                    break;
+                case RULE_REMOVED:
+                    // In case of rule removal, flow statistics are removed from the store
+                    log.info("Rule removed: {}", rule.id());
+                    statsStore.removeFlowStatistic(rule);
+                    break;
+                default:
+                    log.warn("Unknown flow rule event");
+            }
+        }
+
+        @Override
+        public boolean isRelevant(FlowRuleEvent event) {
+            /*
+            *  Check if the rule event is relevant and it needs to be managed
+             * A Rule event is relevant if the flow rule it refers to is
+             * part of one of the monitored intents
+             */
+            FlowRule rule = event.subject();
+            for (Map.Entry<ApplicationId, Map<Key, ConnectivityIntent>> entry : monitoredIntents.entrySet()) {
+                for (Key key : entry.getValue().keySet()) {
+                    List<Intent> ints =  intentService.getInstallableIntents(key);
+                    for (Intent i : ints) {
+                        if (i instanceof FlowRuleIntent
+                                && ((FlowRuleIntent) i).flowRules().contains(rule)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteService.java b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteService.java
new file mode 100644
index 0000000..b674149
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/IntentMonitorAndRerouteService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.imr.data.Route;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.intent.Key;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Intent Monitor and Reroute ONOS service.
+ */
+public interface IntentMonitorAndRerouteService {
+
+    /**
+     * Starts to monitor an intent.
+     * If the intent is not already submitted to the intent subsystem
+     * it memorizes the key and it will start to monitor it as soon as it will be installed
+     * @param intentKey Key of the intent to monitor
+     * @return true, false only if the intent is of one of the not currently supported type
+     */
+    boolean startMonitorIntent(Key intentKey);
+
+    /**
+     * Stops to monitor an intent.
+     * @param intentKey Key of the intent you want to stop the monitoring.
+     * @return false if the intent key passed is not one of the tracked intent, true otherwise.
+     */
+    boolean stopMonitorIntent(Key intentKey);
+
+    /**
+     * Applies a new route to a monitored intent.
+     * @param route Route you want to apply.
+     * @return False in case Application ID or Intent Key are not tracked by IMR
+     */
+    boolean applyPath(Route route);
+
+    /**
+     * Returns the statistics of all the monitored intents.
+     * @return Statistics (in terms of flow entries) of all the monitored intents.
+     */
+    Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats();
+
+    /**
+     * Returns the statistics of all the monitored intents submitted by a specific application.
+     * @param appId Application id of the monitored intent.
+     * @return Statistics (in terms of flow entries) of the requested intents.
+     */
+    Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId);
+
+    /**
+     * Returns the statistics of a specific monitored intent.
+     * @param appId Application id of the monitored intent.
+     * @param intentKey key of the monitored intent.
+     * @return Statistics (in terms of flow entries) of the requested intent.
+     */
+    Map<ApplicationId, Map<Key, List<FlowEntry>>> getStats(ApplicationId appId, Key intentKey);
+
+    /**
+     * Returns the monitored intents in terms of key and connect points.
+     * @return Intents monitored identified by the application id and
+     * the intent key, plus the endpoints of that intent.
+     */
+    Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents();
+
+    /**
+     * Returns the monitored intents submitted by a specific application.
+     * @param appId Application id of the application to extract the monitored intents.
+     * @return Intents monitored identified by the application id and
+     * the intent key, plus the endpoints of that intent.
+     */
+    Map<ApplicationId, Map<Key, Pair<Set<ElementId>, Set<ElementId>>>> getMonitoredIntents(ApplicationId appId);
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/Path.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/Path.java
new file mode 100644
index 0000000..97de631
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/Path.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.HostId;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Representation of a path in terms of list of elements that it has to traverse
+ * and weight.
+ */
+public class Path implements Comparable {
+    private List<ElementId> path;
+    private float weight;
+
+    /**
+     * Returns the list of elements composing the path.
+     * @return the actual path
+     */
+    public List<ElementId> path() {
+        return path;
+    }
+
+    /**
+     * Returns the weight related to the path.
+     * @return the weight
+     */
+    public float weight() {
+        return weight;
+    }
+
+    /**
+     * Creates a Path using Jackson from a JSON Object.
+     * @param path List of element id representig the path.
+     * @param weight Weight related to the path.
+     */
+    @JsonCreator
+    public Path(@JsonProperty("path") List<String> path,
+                @JsonProperty("weight") float weight) {
+        this.path = new ArrayList<>();
+
+        path.forEach(deviceName -> {
+            try {
+                this.path.add((HostId.hostId(deviceName)));
+            } catch (IllegalArgumentException e) {
+                this.path.add(DeviceId.deviceId(deviceName));
+            }
+        });
+        this.weight = weight;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("Path", this.path())
+                .toString();
+    }
+
+    /**
+     * Compare two paths in terms of weight.
+     * @param o Object to compare
+     * @return the comparison result
+     */
+    @Override
+    public int compareTo(Object o) {
+        Path s = (Path) o;
+        if (this.weight < s.weight) {
+            return -1;
+        }
+        if (this.weight > s.weight) {
+            return 1;
+        }
+        return 0;
+    }
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/Route.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/Route.java
new file mode 100644
index 0000000..d39f2aee
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/Route.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.data;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.intent.Key;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Representation of a route submitted by the off-platform application
+ * to be applied to an existing intent.
+ * It is composed by the key and the application id of the intent to modify
+ * and a list of possible {@link Path}.
+ */
+public class Route {
+    private Key key;
+    private ApplicationId appId;
+    private List<Path> paths;
+
+    /**
+     * Returns the intent key the route refers to.
+     * @return the intent key
+     */
+    public Key key() {
+        return key;
+    }
+
+    /**
+     * Returns the Application ID of the intent that has to be modified.
+     * @return the Application ID
+     */
+    public ApplicationId appId() {
+        return appId;
+    }
+
+    /**
+     * Returns the list of the {@link Path} on which the intent has to be routed.
+     * @return the list of path
+     */
+    public List<Path> paths() {
+        return paths;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("IntentKey", this.key)
+                .add("ApplicationId", this.appId)
+                .add("Paths", this.paths)
+                .toString();
+    }
+
+    /**
+     * Creates the route using Jackson from a JSON Object.
+     * @param iKey the intent key
+     * @param appId application id
+     * @param paths list of paths
+     */
+    @JsonCreator
+    public Route(@JsonProperty("key") String iKey,
+                @JsonProperty("appId") Map<String, String> appId,
+                @JsonProperty("paths") List<Path> paths) {
+        this.paths = paths;
+        this.appId = new DefaultApplicationId(Integer.valueOf(appId.get("id")), appId.get("name"));
+        this.key = Key.of(iKey, this.appId);
+    }
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/RoutingConfigurations.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/RoutingConfigurations.java
new file mode 100644
index 0000000..6d955d1
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/RoutingConfigurations.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.imr.data;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.List;
+
+/**
+ * Representation of the routing confiuration submitted by the off-platform application
+ * It is composed by a list of {@link Route}.
+ */
+public class RoutingConfigurations {
+    public List<Route> routingList;
+
+    /**
+     * Returns the list of all the routing submitted by the off-platform application.
+     * @return the routing list
+     */
+    public List<Route> routingList() {
+        return routingList;
+    }
+
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("RoutingList", this.routingList)
+                .toString();
+    }
+
+}
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/data/package-info.java b/apps/imr/app/src/main/java/org/onosproject/imr/data/package-info.java
new file mode 100644
index 0000000..961cc1d
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/data/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Data support classes for Intent Monitor and Reroute.
+ */
+package org.onosproject.imr.data;
\ No newline at end of file
diff --git a/apps/imr/app/src/main/java/org/onosproject/imr/package-info.java b/apps/imr/app/src/main/java/org/onosproject/imr/package-info.java
new file mode 100644
index 0000000..1a5e154
--- /dev/null
+++ b/apps/imr/app/src/main/java/org/onosproject/imr/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Intent Monitor and Reroute application.
+ */
+package org.onosproject.imr;
diff --git a/apps/imr/pom.xml b/apps/imr/pom.xml
new file mode 100644
index 0000000..ae5dc00
--- /dev/null
+++ b/apps/imr/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017 Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>onos-imr</artifactId>
+    <packaging>pom</packaging>
+
+    <description>Intent Monitoring and Rerouting application</description>
+
+    <modules>
+        <module>api</module>
+        <module>app</module>
+    </modules>
+
+</project>
diff --git a/apps/pom.xml b/apps/pom.xml
index beb8c16..e5996db 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -103,6 +103,7 @@
         <module>odtn</module>
         <module>mcast</module>
         <module>layout</module>
+        <module>imr</module>
     </modules>
 
     <properties>
diff --git a/modules.defs b/modules.defs
index be9555f..ccbc538 100644
--- a/modules.defs
+++ b/modules.defs
@@ -242,6 +242,7 @@
     '//apps/odtn:onos-apps-odtn-oar',
     '//apps/mcast:onos-apps-mcast-oar',
     '//apps/layout:onos-apps-layout-oar',
+    '//apps/imr:onos-apps-imr-oar',
 ]
 
 PROTOCOL_APPS = [
@@ -272,6 +273,8 @@
     '//apps/routing-api:onos-apps-routing-api',
     '//apps/dhcp/api:onos-apps-dhcp-api',
     '//apps/dhcp/app:onos-apps-dhcp-app',
+    '//apps/imr/api:onos-apps-imr-api',
+    '//apps/imr/app:onos-apps-imr-app',
     '//apps/dhcprelay:onos-apps-dhcprelay',
     '//apps/fwd:onos-apps-fwd',
     '//apps/iptopology-api:onos-apps-iptopology-api',