You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by jk...@apache.org on 2022/07/28 13:29:49 UTC

[unomi] branch master updated: UNOMI-203, UNOMI-632, UNOMI-633, UNOMI-634: improve integrations test with possibility to test migrations (#464)

This is an automated email from the ASF dual-hosted git repository.

jkevan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git


The following commit(s) were added to refs/heads/master by this push:
     new 53cf44717 UNOMI-203, UNOMI-632, UNOMI-633, UNOMI-634: improve integrations test with possibility to test migrations (#464)
53cf44717 is described below

commit 53cf447177ffb78dd487b85db72c95216de95058
Author: kevan Jahanshahi <ke...@jahia.com>
AuthorDate: Thu Jul 28 15:29:44 2022 +0200

    UNOMI-203, UNOMI-632, UNOMI-633, UNOMI-634: improve integrations test with possibility to test migrations (#464)
    
    * UNOMI-203: improve integration test to use KarafTestSupport
    
    * UNOMI-203: improve integration test to use KarafTestSupport
    
    * UNOMI-633: implement migration configuration handling and silent migration option
    
    * UNOMI-632: implement migration test for 2.0.0 with coverage on actual migrated data
    
    * UNOMI-632: migrate remaining indices that need a reindexation by generify the segment migration script to handle others indices in the same scenario
    
    * Oops
    
    * UNOMI-632: add a bit of documentation on migration testing, for Unomi developers
    
    * UNOMI-633: update documentation regarding migration command
    
    * UNOMI-633: rename 'silent' into 'skipConfirmation' sounds more clear
---
 itests/README.md                                   | 178 ++++++++++++++++-
 itests/pom.xml                                     | 156 ++++++++-------
 .../test/java/org/apache/unomi/itests/AllITs.java  |   2 +
 .../test/java/org/apache/unomi/itests/BaseIT.java  | 184 ++++++++---------
 .../test/java/org/apache/unomi/itests/BasicIT.java |  40 ++--
 .../apache/unomi/itests/ConditionEvaluatorIT.java  |   5 -
 .../org/apache/unomi/itests/ContextServletIT.java  |  92 +++------
 .../unomi/itests/CopyPropertiesActionIT.java       |   7 -
 .../org/apache/unomi/itests/EventServiceIT.java    |   8 -
 .../unomi/itests/GroovyActionsServiceIT.java       |  16 --
 .../apache/unomi/itests/IncrementPropertyIT.java   |  16 --
 .../org/apache/unomi/itests/InputValidationIT.java |  42 ++--
 .../java/org/apache/unomi/itests/JSONSchemaIT.java |  14 +-
 .../org/apache/unomi/itests/ModifyConsentIT.java   |   8 -
 .../test/java/org/apache/unomi/itests/PatchIT.java |  19 --
 .../org/apache/unomi/itests/ProfileExportIT.java   |   7 -
 .../apache/unomi/itests/ProfileImportActorsIT.java |   7 -
 .../apache/unomi/itests/ProfileImportBasicIT.java  |   5 -
 .../unomi/itests/ProfileImportRankingIT.java       |   7 -
 .../unomi/itests/ProfileImportSurfersIT.java       |   7 -
 .../org/apache/unomi/itests/ProfileMergeIT.java    |  16 --
 .../org/apache/unomi/itests/ProfileServiceIT.java  |  12 --
 .../itests/ProfileServiceWithoutOverwriteIT.java   |  14 +-
 .../unomi/itests/PropertiesUpdateActionIT.java     |  10 -
 .../org/apache/unomi/itests/RuleServiceIT.java     |   8 -
 .../test/java/org/apache/unomi/itests/ScopeIT.java |   8 -
 .../java/org/apache/unomi/itests/SecurityIT.java   |   4 +-
 .../java/org/apache/unomi/itests/SegmentIT.java    |  16 --
 .../org/apache/unomi/itests/SendEventActionIT.java |   7 -
 .../apache/unomi/itests/graphql/BaseGraphQLIT.java |  13 +-
 .../unomi/itests/graphql/GraphQLEventIT.java       |  20 +-
 .../apache/unomi/itests/graphql/GraphQLListIT.java |   4 -
 .../itests/graphql/GraphQLProfileAliasesIT.java    |   4 -
 .../unomi/itests/graphql/GraphQLProfileIT.java     |   8 +-
 .../itests/graphql/GraphQLProfilePropertiesIT.java |   4 -
 .../unomi/itests/graphql/GraphQLSegmentIT.java     |   6 +-
 .../unomi/itests/graphql/GraphQLSourceIT.java      |   6 +-
 .../unomi/itests/graphql/GraphQLTopicIT.java       |   6 +-
 .../apache/unomi/itests/graphql/GraphQLViewIT.java |  16 --
 .../unomi/itests/graphql/GraphQLWebSocketIT.java   |   4 +-
 .../unomi/itests/migration/Migrate16xTo200IT.java  | 218 +++++++++++++++++++++
 .../migration/create_snapshots_repository.json     |   6 +
 .../migration/org.apache.unomi.migration.cfg       |  22 +++
 .../resources/migration/snapshots_repository.zip   | Bin 0 -> 709502 bytes
 kar/src/main/feature/feature.xml                   |   1 +
 manual/src/main/asciidoc/shell-commands.adoc       |  10 +-
 .../ElasticSearchPersistenceServiceImpl.java       |  11 --
 pom.xml                                            |   1 +
 tools/shell-commands/pom.xml                       |  24 +++
 .../apache/unomi/shell/migration/Migration.java    |   2 +-
 .../unomi/shell/migration/MigrationConfig.java     | 104 ++++++++++
 .../shell/migration/MigrationConfigProperty.java}  |  26 ++-
 .../unomi/shell/migration/actions/Migrate.java     |  33 ++--
 .../unomi/shell/migration/impl/MigrationTo121.java |   5 +-
 .../unomi/shell/migration/impl/MigrationTo122.java |   5 +-
 .../unomi/shell/migration/impl/MigrationTo150.java |   7 +-
 .../unomi/shell/migration/impl/MigrationTo200.java |  40 ++--
 .../shell/migration/utils/MigrationUtils.java      |  44 ++++-
 ...roovy => migrate-2.0.0-01-globalReindex.groovy} |  15 +-
 .../migrate-2.0.0-03-profileReindex.groovy         |   9 +-
 .../migrate-2.0.0-04-eventsReindex.groovy          |   8 +-
 .../main/resources/org.apache.unomi.migration.cfg  |  22 +++
 .../requestBody/2.0.0/base_index_mapping.json      |  23 +++
 .../resources/requestBody/2.0.0/event_index.json   |  87 --------
 .../resources/requestBody/2.0.0/profile_index.json |  76 -------
 .../resources/requestBody/2.0.0/scoring_index.json |  68 -------
 .../resources/requestBody/2.0.0/segment_index.json |  64 ------
 .../main/resources/requestBody/scopeMapping.json   |  44 -----
 68 files changed, 997 insertions(+), 984 deletions(-)

diff --git a/itests/README.md b/itests/README.md
index 845ea669c..7be12dcd0 100644
--- a/itests/README.md
+++ b/itests/README.md
@@ -38,16 +38,16 @@ have what you expect and it could work fine on your machine but not on others.
 
 If possible clean what your test created at the end of its execution or at the very least make sure to use unique IDs  
 
-When you need a service from Unomi to execute your test inject it with a filer:  
+When you need a service from Unomi to execute your test, you can add it to the BaseIT code:  
 ```java
-@Inject @Filter(timeout = 60000)
-protected ProfileService profileService;
+@Before
+public void waitForStartup() throws InterruptedException {
+    // ...
+    // init unomi services that are available once unomi:start have been called
+    persistenceService=getOsgiService(PersistenceService.class, 600000);
+}
 ``` 
-This will ensure the service is available before starting the test and if you need a resource like an URL you can do something like this:  
-```java
-@Inject @Filter(value="(configDiscriminator=IMPORT)", timeout = 60000)
-protected ImportExportConfigurationService<ImportConfiguration> importConfigurationService;
-```
+This will ensure the service is available before starting the test.
 ## Running integration tests
 
 You can run the integration tests along with the build by doing:
@@ -82,4 +82,164 @@ https://maven.apache.org/surefire/maven-failsafe-plugin/examples/single-test.htm
 
 Here's an example:
 
-    mvn clean install -Dit.karaf.debug=hold:true -Dit.test=org.apache.unomi.itests.graphql.GraphQLEventIT
\ No newline at end of file
+    mvn clean install -Dit.karaf.debug=hold:true -Dit.test=org.apache.unomi.itests.graphql.GraphQLEventIT
+
+## Migration tests
+
+Migration can now be tested, by reusing an ElasticSearch snapshot. 
+The snapshot should be from a Unomi version where you want to start the migration from.
+
+The snapshot is copied to the /target folder using a maven ant plugin:
+
+    <plugin>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>1.8</version>
+        <executions>
+            <execution>
+                <phase>generate-resources</phase>
+                <configuration>
+                <tasks>
+                    <unzip src="${project.basedir}/src/test/resources/migration/snapshots_repository.zip" dest="${project.build.directory}" />
+                </tasks>
+                </configuration>
+                <goals>
+                    <goal>run</goal>
+                </goals>
+            </execution>
+        </executions>
+    </plugin>
+
+Also the ElasticSearch maven plugin is configured to allow this snapshot repository using conf:
+
+    <path.repo>${project.build.directory}/snapshots_repository</path.repo>
+
+Now that migration accept configuration file we can provide it, this allows to avoid the migration process to prompt questions (in BaseIT configuration):
+
+    replaceConfigurationFile("etc/org.apache.unomi.migration.cfg", new File("src/test/resources/migration/org.apache.unomi.migration.cfg")),
+
+The config should contain all the required prop for the migration you want to do, example:
+
+    esAddress = http://localhost:9400
+    httpClient.trustAllCertificates = true
+    indexPrefix = context
+
+Then in the first Test of the suite you can restore the Snapshot and run the migration cmd, like this:
+
+```java
+public class Migrate16xTo200IT extends BaseIT {
+
+    @Override
+    @Before
+    public void waitForStartup() throws InterruptedException {
+
+        // Restore snapshot from 1.6.x
+        try (CloseableHttpClient httpClient = HttpUtils.initHttpClient(true)) {
+            // Create snapshot repo
+            HttpUtils.executePutRequest(httpClient, "http://localhost:9400/_snapshot/snapshots_repository/", resourceAsString("migration/create_snapshots_repository.json"), null);
+            // Get snapshot, insure it exists
+            String snapshot = HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/_snapshot/snapshots_repository/snapshot_1.6.x", null);
+            if (snapshot == null || !snapshot.contains("snapshot_1.6.x")) {
+                throw new RuntimeException("Unable to retrieve 1.6.x snapshot for ES restore");
+            }
+            // Restore the snapshot
+            HttpUtils.executePostRequest(httpClient, "http://localhost:9400/_snapshot/snapshots_repository/snapshot_1.6.x/_restore?wait_for_completion=true", "{}", null);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        // Do migrate the data set
+        executeCommand("unomi:migrate 1.6.0 true");
+        // Call super for starting Unomi and wait for the complete startup
+        super.waitForStartup();
+    }
+
+    @After
+    public void cleanup() throws InterruptedException {
+        // Do some cleanup for next tests
+    }
+
+    @Test
+    public void checkMigratedData() throws Exception {
+        // call Unomi services to check the migrated data is correct.
+    }
+}
+``` 
+
+### How to update a migration test ElasticSearch Snapshot ?
+
+In the following example we want to modify the snapshot: `snapshot_1.6.x`.
+This snapshot has been done on Unomi 1.6.x using ElasticSearch 7.11.0. 
+So we will set up locally those servers in the exact same versions.
+(For now just download them and do not start them yet.)
+
+First we need to extract the zip of the snapshot repository from the test resources:
+
+    /src/test/resources/migration/snapshots_repository.zip
+
+In my case I unzip it to:
+
+    /servers/elasticsearch-7.11.0/
+
+So I have the following folders structure:
+
+    /servers/elasticsearch-7.11.0/snapshots_repository/snapshots
+
+Now we need to configure our ElasticSearch server to allow this path as repo, edit the `elasticsearch.yml` to add this:
+
+    path:
+        repo:
+            - /servers/elasticsearch-7.11.0/snapshots_repository
+
+Start ElasticSearch server.
+Now we have to add the snapshot repository, do the following request on your ElasticSearch instance:
+
+    PUT /_snapshot/snapshots_repository/
+    {
+        "type": "fs",
+        "settings": {
+            "location": "snapshots"
+        }
+    }
+
+Now we need to restore the snapshot we want to modify, 
+but first let's try to see if the snapshot with the id `snapshot_1.6.x` correctly exists:
+
+    GET /_snapshot/snapshots_repository/snapshot_1.6.x
+
+If the snapshot exists we can restore it:
+
+    POST /_snapshot/snapshots_repository/snapshot_1.6.x/_restore?wait_for_completion=true
+    {}
+
+At the end of the previous request ElasticSearch should be ready and our Unomi snapshot is restored to version `1.6.x`.
+Now make sure your Unomi server is correctly configured to connect to your running ElasticSearch, then start the Unomi server.
+In my case it's Unomi version 1.6.0.
+
+Once Unomi started you can perform all the operations you want to be able to add the required data to the next snapshot, like:
+- creating new events
+- creating new profiles with new data to be migrated
+- create rules/segments etc ...
+- anything you want to be part of the new snapshot.
+
+(NOTE: that it is important to add new data to the existing snapshot, but try to not removing things, 
+they are probably used by the actual migration tests already.)
+
+Once you data updated we need to recreate the snapshot, first we delete the old snapshot:
+
+    DELETE /_snapshot/snapshots_repository/snapshot_1.6.x
+
+Then we recreate it:
+
+    PUT /_snapshot/snapshots_repository/snapshot_1.6.x
+
+Once the process finished (check the ElasticSearch logs to see that the snapshot is correctly created), 
+we need to remove the snapshot repository from our local ElasticSearch
+
+    DELETE /_snapshot/snapshots_repository
+
+And the final step is, zipping the new version of the snapshot repository and replace it in the test resources:
+
+    zip -r snapshots_repository.zip /servers/elasticsearch-7.11.0/snapshots_repository
+    cp /servers/elasticsearch-7.11.0/snapshots_repository.zip src/test/resources/migration/snapshots_repository.zip
+
+Now you can modify the migration test class to test that your added data in 1.6.x is correctly migrated in 2.0.0
diff --git a/itests/pom.xml b/itests/pom.xml
index b65bce832..fb1799204 100644
--- a/itests/pom.xml
+++ b/itests/pom.xml
@@ -28,127 +28,118 @@
     <name>Apache Unomi :: Integration Tests</name>
     <description>Apache Unomi Context Server integration tests</description>
 
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.karaf</groupId>
+                <artifactId>karaf-bom</artifactId>
+                <version>${karaf.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
     <dependencies>
+        <!-- Provide the KarafTestSupport -->
         <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi</artifactId>
-            <version>${project.version}</version>
-            <type>tar.gz</type>
+            <groupId>org.apache.karaf.itests</groupId>
+            <artifactId>common</artifactId>
+            <version>${karaf.version}</version>
             <scope>test</scope>
         </dependency>
+        <!-- Define the Apache Karaf version to download and use for the test -->
+        <!-- We use a released version here to avoid SNAPSHOT resolution -->
         <dependency>
             <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-router-karaf-feature</artifactId>
-            <classifier>features</classifier>
+            <artifactId>unomi</artifactId>
             <version>${project.version}</version>
-            <type>xml</type>
+            <type>tar.gz</type>
             <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.karaf</groupId>
+                    <artifactId>org.apache.karaf.client</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.httpcomponents</groupId>
+                    <artifactId>httpclient-osgi</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
+        <!-- Required to use shell commands in the tests -->
         <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>cdp-graphql-feature</artifactId>
-            <classifier>features</classifier>
-            <version>${project.version}</version>
-            <type>xml</type>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.core</artifactId>
             <scope>test</scope>
         </dependency>
+        <!-- Provide the PaxExam Karaf support -->
         <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-persistence-spi</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-karaf</artifactId>
+            <scope>test</scope>
         </dependency>
+        <!-- Provide the PaxExam JUnit extension -->
         <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-wab</artifactId>
-            <version>${project.version}</version>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-lifecycle-watcher</artifactId>
-            <version>${project.version}</version>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-atinject_1.0_spec</artifactId>
+            <version>1.2</version>
             <scope>test</scope>
         </dependency>
-
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <scope>provided</scope>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.codehaus.groovy</groupId>
-            <artifactId>groovy</artifactId>
-            <version>${groovy.version}</version>
-            <scope>provided</scope>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.hamcrest</artifactId>
+            <version>1.3_1</version>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient-osgi</artifactId>
-            <type>bundle</type>
+            <version>4.5.5</version>
         </dependency>
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpcore-osgi</artifactId>
+            <version>4.4.9</version>
         </dependency>
         <dependency>
             <groupId>org.eclipse.jetty.websocket</groupId>
             <artifactId>websocket-client</artifactId>
             <version>9.4.28.v20200408</version>
-            <type>bundle</type>
         </dependency>
-
-
-        <!-- Dependencies for pax exam karaf container -->
         <dependency>
-            <groupId>org.ops4j.pax.exam</groupId>
-            <artifactId>pax-exam-container-karaf</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.ops4j.pax.exam</groupId>
-            <artifactId>pax-exam-junit4</artifactId>
+            <groupId>org.apache.unomi</groupId>
+            <artifactId>unomi-lifecycle-watcher</artifactId>
+            <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.ops4j.pax.exam</groupId>
-            <artifactId>pax-exam</artifactId>
+            <groupId>org.apache.unomi</groupId>
+            <artifactId>unomi-persistence-spi</artifactId>
+            <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.ops4j.pax.url</groupId>
-            <artifactId>pax-url-aether</artifactId>
+            <groupId>org.apache.unomi</groupId>
+            <artifactId>shell-commands</artifactId>
+            <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>javax.inject</groupId>
-            <artifactId>javax.inject</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>osgi.core</artifactId>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
+            <version>${groovy.version}</version>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-simple</artifactId>
-            <version>1.6.6</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.ops4j.pax.url</groupId>
-            <artifactId>pax-url-wrap</artifactId>
-            <classifier>uber</classifier>
-            <version>2.5.4</version>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <profiles>
@@ -196,6 +187,23 @@
                             </execution>
                         </executions>
                     </plugin>
+                    <plugin>
+                        <artifactId>maven-antrun-plugin</artifactId>
+                        <version>1.8</version>
+                        <executions>
+                            <execution>
+                                <phase>generate-resources</phase>
+                                <configuration>
+                                    <tasks>
+                                        <unzip src="${project.basedir}/src/test/resources/migration/snapshots_repository.zip" dest="${project.build.directory}" />
+                                    </tasks>
+                                </configuration>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
                     <plugin>
                         <groupId>com.github.alexcojocaru</groupId>
                         <artifactId>elasticsearch-maven-plugin</artifactId>
@@ -213,6 +221,7 @@
                             </environmentVariables>
                             <instanceSettings>
                                 <properties>
+                                    <path.repo>${project.build.directory}/snapshots_repository</path.repo>
                                     <cluster.routing.allocation.disk.threshold_enabled>false</cluster.routing.allocation.disk.threshold_enabled>
                                     <http.cors.allow-origin>*</http.cors.allow-origin>
                                     <http.cors.allow-methods>OPTIONS,HEAD,GET,POST,PUT,DELETE</http.cors.allow-methods>
@@ -249,6 +258,9 @@
                             <includes>
                                 <include>**/*AllITs.java</include>
                             </includes>
+                            <systemPropertyVariables>
+                                <my.system.property>foo</my.system.property>
+                            </systemPropertyVariables>
                         </configuration>
                         <executions>
                             <execution>
diff --git a/itests/src/test/java/org/apache/unomi/itests/AllITs.java b/itests/src/test/java/org/apache/unomi/itests/AllITs.java
index c0f858e04..1db16621a 100644
--- a/itests/src/test/java/org/apache/unomi/itests/AllITs.java
+++ b/itests/src/test/java/org/apache/unomi/itests/AllITs.java
@@ -17,6 +17,7 @@
 
 package org.apache.unomi.itests;
 
+import org.apache.unomi.itests.migration.Migrate16xTo200IT;
 import org.apache.unomi.itests.graphql.*;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -29,6 +30,7 @@ import org.junit.runners.Suite.SuiteClasses;
  */
 @RunWith(Suite.class)
 @SuiteClasses({
+        Migrate16xTo200IT.class,
         BasicIT.class,
         ConditionEvaluatorIT.class,
         ConditionESQueryBuilderIT.class,
diff --git a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
index 0c400b920..dc103951d 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
@@ -42,14 +42,20 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.karaf.itests.KarafTestSupport;
 import org.apache.unomi.api.Item;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.rules.Rule;
-import org.apache.unomi.api.services.DefinitionsService;
-import org.apache.unomi.api.services.RulesService;
+import org.apache.unomi.api.services.*;
+import org.apache.unomi.groovy.actions.services.GroovyActionsService;
 import org.apache.unomi.lifecycle.BundleWatcher;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
 import org.apache.unomi.persistence.spi.PersistenceService;
+import org.apache.unomi.router.api.ExportConfiguration;
+import org.apache.unomi.router.api.ImportConfiguration;
+import org.apache.unomi.router.api.services.ImportExportConfigurationService;
+import org.apache.unomi.schema.api.SchemaService;
+import org.apache.unomi.services.UserListService;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -58,14 +64,12 @@ import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
 import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.karaf.container.internal.JavaVersionUtil;
 import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
 import org.ops4j.pax.exam.options.MavenArtifactUrlReference;
 import org.ops4j.pax.exam.options.extra.VMOption;
 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
 import org.ops4j.pax.exam.spi.reactors.PerSuite;
 import org.ops4j.pax.exam.util.Filter;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
@@ -97,15 +101,10 @@ import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
+import java.util.stream.Stream;
 
-import static org.ops4j.pax.exam.CoreOptions.maven;
 import static org.ops4j.pax.exam.CoreOptions.systemProperty;
-import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
-import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
-import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
-import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
-import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
-import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.replaceConfigurationFile;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.*;
 
 /**
  * Base class for integration tests.
@@ -114,13 +113,10 @@ import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.replaceCo
  */
 @RunWith(PaxExam.class)
 @ExamReactorStrategy(PerSuite.class)
-public abstract class BaseIT {
+public abstract class BaseIT extends KarafTestSupport {
 
     private final static Logger LOGGER = LoggerFactory.getLogger(BaseIT.class);
 
-    protected static final String HTTP_PORT = "8181";
-    protected static final String URL = "http://localhost:" + HTTP_PORT;
-    protected static final String KARAF_DIR = "target/exam";
     protected static final String UNOMI_KEY = "670c26d1cc413346c3b2fd9ce65dab41";
     protected static final ContentType JSON_CONTENT_TYPE = ContentType.create("application/json");
     protected static final String BASE_URL = "http://localhost";
@@ -131,6 +127,7 @@ public abstract class BaseIT {
     protected static final int DEFAULT_TRYING_TRIES = 30;
 
     protected final static ObjectMapper objectMapper;
+    protected static boolean unomiStarted = false;
 
     static {
         objectMapper = new ObjectMapper();
@@ -139,37 +136,67 @@ public abstract class BaseIT {
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
     }
 
-    @Inject
-    @Filter(timeout = 600000)
     protected PersistenceService persistenceService;
-
-    @Inject
-    @Filter(timeout = 600000)
     protected RulesService rulesService;
-
-    @Inject
-    @Filter(timeout = 600000)
     protected DefinitionsService definitionsService;
+    protected ProfileService profileService;
+    protected EventService eventService;
+    protected BundleWatcher bundleWatcher;
+    protected GroovyActionsService groovyActionsService;
+    protected SegmentService segmentService;
+    protected SchemaService schemaService;
+    protected ScopeService scopeService;
+    protected PatchService patchService;
+    protected ImportExportConfigurationService<ImportConfiguration> importConfigurationService;
+    protected ImportExportConfigurationService<ExportConfiguration> exportConfigurationService;
+    protected UserListService userListService;
+    protected TopicService topicService;
 
     @Inject
     protected BundleContext bundleContext;
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected BundleWatcher bundleWatcher;
-
     @Inject
     @Filter(timeout = 600000)
     protected ConfigurationAdmin configurationAdmin;
 
-    private CloseableHttpClient httpClient;
+    protected CloseableHttpClient httpClient;
 
     @Before
     public void waitForStartup() throws InterruptedException {
+        // disable retry
+        retry = new KarafTestSupport.Retry(false);
+
+        // Start Unomi if not already done
+        if (!unomiStarted) {
+            executeCommand("unomi:start");
+            unomiStarted = true;
+        }
+
+        // Wait for startup complete
+        bundleWatcher = getOsgiService(BundleWatcher.class, 600000);
         while (!bundleWatcher.isStartupComplete() || !bundleWatcher.allAdditionalBundleStarted()) {
             LOGGER.info("Waiting for startup to complete...");
             Thread.sleep(1000);
         }
+
+        // init unomi services that are available once unomi:start have been called
+        persistenceService = getOsgiService(PersistenceService.class, 600000);
+        rulesService = getOsgiService(RulesService.class, 600000);
+        definitionsService = getOsgiService(DefinitionsService.class, 600000);
+        profileService = getOsgiService(ProfileService.class, 600000);
+        eventService = getOsgiService(EventService.class, 600000);
+        groovyActionsService = getOsgiService(GroovyActionsService.class, 600000);
+        segmentService = getOsgiService(SegmentService.class, 600000);
+        schemaService = getOsgiService(SchemaService.class, 600000);
+        scopeService = getOsgiService(ScopeService.class, 600000);
+        patchService = getOsgiService(PatchService.class, 600000);
+        userListService = getOsgiService(UserListService.class, 600000);
+        topicService = getOsgiService(TopicService.class, 600000);
+        patchService = getOsgiService(PatchService.class, 600000);
+        importConfigurationService = getOsgiService(ImportExportConfigurationService.class, "(configDiscriminator=IMPORT)", 600000);
+        exportConfigurationService = getOsgiService(ImportExportConfigurationService.class, "(configDiscriminator=EXPORT)", 600000);
+
+        // init httpClient
         httpClient = initHttpClient();
     }
 
@@ -198,55 +225,53 @@ public abstract class BaseIT {
         Thread.sleep(1000);
     }
 
-    @Configuration
-    public Option[] config() throws InterruptedException {
-
-        MavenArtifactUrlReference karafUrl = maven().groupId("org.apache.unomi").artifactId("unomi").type("tar.gz").versionAsInProject();
-
-        List<Option> options = new ArrayList<>();
+    @Override
+    public MavenArtifactUrlReference getKarafDistribution() {
+        return CoreOptions.maven().groupId("org.apache.unomi").artifactId("unomi").versionAsInProject().type("tar.gz");
+    }
 
-        Option[] commonOptions = new Option[] {
-                karafDistributionConfiguration().frameworkUrl(karafUrl).unpackDirectory(new File(KARAF_DIR)).useDeployFolder(true),
+    @Configuration
+    public Option[] config() {
+        System.out.println("==== Configuring container");
+        Option[] options = new Option[]{
                 replaceConfigurationFile("etc/org.apache.unomi.router.cfg", new File("src/test/resources/org.apache.unomi.router.cfg")),
+                replaceConfigurationFile("etc/org.apache.unomi.migration.cfg", new File("src/test/resources/migration/org.apache.unomi.migration.cfg")),
+
                 replaceConfigurationFile("data/tmp/1-basic-test.csv", new File("src/test/resources/1-basic-test.csv")),
                 replaceConfigurationFile("data/tmp/recurrent_import/2-surfers-test.csv", new File("src/test/resources/2-surfers-test.csv")),
-                replaceConfigurationFile("data/tmp/recurrent_import/3-surfers-overwrite-test.csv",
-                        new File("src/test/resources/3-surfers-overwrite-test.csv")),
-                replaceConfigurationFile("data/tmp/recurrent_import/4-surfers-delete-test.csv",
-                        new File("src/test/resources/4-surfers-delete-test.csv")),
+                replaceConfigurationFile("data/tmp/recurrent_import/3-surfers-overwrite-test.csv", new File("src/test/resources/3-surfers-overwrite-test.csv")),
+                replaceConfigurationFile("data/tmp/recurrent_import/4-surfers-delete-test.csv", new File("src/test/resources/4-surfers-delete-test.csv")),
                 replaceConfigurationFile("data/tmp/recurrent_import/5-ranking-test.csv", new File("src/test/resources/5-ranking-test.csv")),
                 replaceConfigurationFile("data/tmp/recurrent_import/6-actors-test.csv", new File("src/test/resources/6-actors-test.csv")),
                 replaceConfigurationFile("data/tmp/testLogin.json", new File("src/test/resources/testLogin.json")),
                 replaceConfigurationFile("data/tmp/testCopyProperties.json", new File("src/test/resources/testCopyProperties.json")),
-                replaceConfigurationFile("data/tmp/testCopyPropertiesWithoutSystemTags.json",
-                        new File("src/test/resources/testCopyPropertiesWithoutSystemTags.json")),
-                replaceConfigurationFile("data/tmp/testLoginEventCondition.json",
-                        new File("src/test/resources/testLoginEventCondition.json")),
-                replaceConfigurationFile("data/tmp/testClickEventCondition.json",
-                        new File("src/test/resources/testClickEventCondition.json")),
+                replaceConfigurationFile("data/tmp/testCopyPropertiesWithoutSystemTags.json", new File("src/test/resources/testCopyPropertiesWithoutSystemTags.json")),
+                replaceConfigurationFile("data/tmp/testLoginEventCondition.json", new File("src/test/resources/testLoginEventCondition.json")),
+                replaceConfigurationFile("data/tmp/testClickEventCondition.json", new File("src/test/resources/testClickEventCondition.json")),
                 replaceConfigurationFile("data/tmp/testRuleGroovyAction.json", new File("src/test/resources/testRuleGroovyAction.json")),
-                replaceConfigurationFile("data/tmp/groovy/UpdateAddressAction.groovy",
-                        new File("src/test/resources/groovy/UpdateAddressAction.groovy")), keepRuntimeFolder(),
-                // configureConsole().ignoreLocalConsole(),
-                logLevel(LogLevel.INFO),
-                editConfigurationFilePut("etc/custom.system.properties", "org.apache.unomi.graphql.feature.activated", "true"),
+                replaceConfigurationFile("data/tmp/groovy/UpdateAddressAction.groovy", new File("src/test/resources/groovy/UpdateAddressAction.groovy")),
+
                 editConfigurationFilePut("etc/org.ops4j.pax.logging.cfg", "log4j2.rootLogger.level", "INFO"),
                 editConfigurationFilePut("etc/org.apache.karaf.features.cfg", "serviceRequirements", "disable"),
-                //                editConfigurationFilePut("etc/org.ops4j.pax.web.cfg", "org.osgi.service.http.port", HTTP_PORT),
-                //                systemProperty("org.osgi.service.http.port").value(HTTP_PORT),
+                editConfigurationFilePut("etc/system.properties", "my.system.property", System.getProperty("my.system.property")),
+                editConfigurationFilePut("etc/custom.system.properties", "org.apache.unomi.graphql.feature.activated", "true"),
+                editConfigurationFilePut("etc/custom.system.properties", "org.apache.unomi.elasticsearch.cluster.name", "contextElasticSearchITests"),
+                editConfigurationFilePut("etc/custom.system.properties", "org.apache.unomi.elasticsearch.addresses", "localhost:9400"),
+
                 systemProperty("org.ops4j.pax.exam.rbc.rmi.port").value("1199"),
-                systemProperty("org.apache.unomi.itests.elasticsearch.transport.port").value("9500"),
-                systemProperty("org.apache.unomi.itests.elasticsearch.cluster.name").value("contextElasticSearchITests"),
-                systemProperty("org.apache.unomi.itests.elasticsearch.http.port").value("9400"),
-                systemProperty("org.apache.unomi.itests.elasticsearch.bootstrap.seccomp").value("false"),
                 systemProperty("org.apache.unomi.hazelcast.group.name").value("cellar"),
                 systemProperty("org.apache.unomi.hazelcast.group.password").value("pass"),
                 systemProperty("org.apache.unomi.hazelcast.network.port").value("5701"),
                 systemProperty("org.apache.unomi.hazelcast.tcp-ip.members").value("127.0.0.1"),
                 systemProperty("org.apache.unomi.hazelcast.tcp-ip.interface").value("127.0.0.1"),
-                systemProperty("unomi.autoStart").value("true"), CoreOptions.bundleStartLevel(100), CoreOptions.frameworkStartLevel(100) };
 
-        options.addAll(Arrays.asList(commonOptions));
+                logLevel(LogLevel.INFO),
+                keepRuntimeFolder(),
+                CoreOptions.bundleStartLevel(100),
+                CoreOptions.frameworkStartLevel(100)
+        };
+        List<Option> karafOptions = new ArrayList<>();
+        karafOptions.addAll(Arrays.asList(options));
 
         String karafDebug = System.getProperty("it.karaf.debug");
         if (karafDebug != null) {
@@ -265,7 +290,7 @@ public abstract class BaseIT {
                     }
                 }
             }
-            options.add(0, debugConfiguration(port, hold));
+            karafOptions.add(0, debugConfiguration(port, hold));
         }
 
         // Jacoco setup
@@ -275,7 +300,7 @@ public abstract class BaseIT {
             final String jacocoOption = "-javaagent:" + agentFile + "=destfile=" + System.getProperty("user.dir")
                     + "/target/jacoco.exec,includes=org.apache.unomi.*";
             LOGGER.info("set jacoco java agent: {}", jacocoOption);
-            options.add(new VMOption(jacocoOption));
+            karafOptions.add(new VMOption(jacocoOption));
         } else {
             LOGGER.warn("Unable to set jacoco agent as {} was not found", agentFile);
         }
@@ -283,34 +308,11 @@ public abstract class BaseIT {
         String customLogging = System.getProperty("it.karaf.customLogging");
         if (customLogging != null) {
             String[] customLoggingParts = customLogging.split(":");
-            options.add(editConfigurationFilePut("etc/org.ops4j.pax.logging.cfg", "log4j2.logger.customLogging.name", customLoggingParts[0]));
-            options.add(editConfigurationFilePut("etc/org.ops4j.pax.logging.cfg", "log4j2.logger.customLogging.level", customLoggingParts[1]));
-        }
-
-        if (JavaVersionUtil.getMajorVersion() >= 9) {
-            Option[] jdk9PlusOptions = new Option[] { new VMOption("--add-reads=java.xml=java.logging"),
-                    new VMOption("--add-exports=java.base/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED"),
-                    new VMOption("--patch-module"),
-                    new VMOption("java.base=lib/endorsed/org.apache.karaf.specs.locator-" + System.getProperty("karaf.version") + ".jar"),
-                    new VMOption("--patch-module"),
-                    new VMOption("java.xml=lib/endorsed/org.apache.karaf.specs.java.xml-" + System.getProperty("karaf.version") + ".jar"),
-                    new VMOption("--add-opens"), new VMOption("java.base/java.security=ALL-UNNAMED"), new VMOption("--add-opens"),
-                    new VMOption("java.base/java.net=ALL-UNNAMED"), new VMOption("--add-opens"),
-                    new VMOption("java.base/java.lang=ALL-UNNAMED"), new VMOption("--add-opens"),
-                    new VMOption("java.base/java.util=ALL-UNNAMED"), new VMOption("--add-opens"),
-                    new VMOption("java.naming/javax.naming.spi=ALL-UNNAMED"), new VMOption("--add-opens"),
-                    new VMOption("java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED"),
-                    new VMOption("--add-exports=java.base/sun.net.www.protocol.http=ALL-UNNAMED"),
-                    new VMOption("--add-exports=java.base/sun.net.www.protocol.https=ALL-UNNAMED"),
-                    new VMOption("--add-exports=java.base/sun.net.www.protocol.jar=ALL-UNNAMED"),
-                    new VMOption("--add-exports=jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED"), new VMOption("-classpath"),
-                    new VMOption("lib/jdk9plus/*" + File.pathSeparator + "lib/boot/*")
-
-            };
-            options.addAll(Arrays.asList(jdk9PlusOptions));
+            karafOptions.add(editConfigurationFilePut("etc/org.ops4j.pax.logging.cfg", "log4j2.logger.customLogging.name", customLoggingParts[0]));
+            karafOptions.add(editConfigurationFilePut("etc/org.ops4j.pax.logging.cfg", "log4j2.logger.customLogging.level", customLoggingParts[1]));
         }
 
-        return options.toArray(new Option[0]);
+        return Stream.of(super.config(), karafOptions.toArray(new Option[karafOptions.size()])).flatMap(Stream::of).toArray(Option[]::new);
     }
 
     protected <T> T keepTrying(String failMessage, Supplier<T> call, Predicate<T> predicate, int timeout, int retries)
@@ -355,7 +357,11 @@ public abstract class BaseIT {
     protected String bundleResourceAsString(final String resourcePath) throws IOException {
         final java.net.URL url = bundleContext.getBundle().getResource(resourcePath);
         if (url != null) {
-            return IOUtils.toString(url);
+            try (InputStream stream = url.openStream()) {
+                return IOUtils.toString(stream);
+            } catch (final Exception e) {
+                throw new RuntimeException(e);
+            }
         } else {
             return null;
         }
@@ -445,7 +451,7 @@ public abstract class BaseIT {
     }
 
     public String getFullUrl(String url) throws Exception {
-        return BASE_URL + ":" + HTTP_PORT + url;
+        return BASE_URL + ":" + getHttpPort() + url;
     }
 
     protected <T> T get(final String url, Class<T> clazz) {
diff --git a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
index 819ce4495..c20a46377 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
@@ -92,14 +92,6 @@ public class BasicIT extends BaseIT {
     private static final String EMAIL_VISITOR_1 = "visitor1@apache.unomi.org";
     private static final String EMAIL_VISITOR_2 = "visitor2@apache.unomi.org";
 
-    @Inject @Filter(timeout = 600000)
-    protected ProfileService profileService;
-    @Inject @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
-    @Inject @Filter(timeout = 600000)
-    protected ScopeService scopeService;
-
     @Before
     public void setUp() throws InterruptedException {
         TestUtils.createScope(TEST_SCOPE, "Test scope", scopeService);
@@ -113,9 +105,15 @@ public class BasicIT extends BaseIT {
     }
 
     @Test
-    public void testContextJS() throws IOException {
+    public void simpleTest() throws Exception {
+        System.out.println("==== System Property in probe bundle: " + System.getProperty("my.system.property"));
+        assertContains("foo", System.getProperty("my.system.property"));
+    }
+
+    @Test
+    public void testContextJS() throws Exception {
         LOGGER.info("Start test testContextJS");
-        HttpUriRequest request = new HttpGet(URL + "/cxs/context.js?sessionId=" + SESSION_ID_0);
+        HttpUriRequest request = new HttpGet(getFullUrl("/cxs/context.js?sessionId=" + SESSION_ID_0));
         request.setHeader("Content-Type", "application/json");
         // The underlying HTTP connection is still held by the response object
         // to allow the response content to be streamed directly from the network socket.
@@ -137,10 +135,10 @@ public class BasicIT extends BaseIT {
     }
 
     @Test
-    public void testContextJSONWithUrlParameter() throws IOException, InterruptedException {
+    public void testContextJSONWithUrlParameter() throws Exception {
         LOGGER.info("Start test testContextJSONWithUrlParameter");
         ContextRequest contextRequest = new ContextRequest();
-        HttpPost request = new HttpPost(URL + "/cxs/context.json?sessionId=" + SESSION_ID_1);
+        HttpPost request = new HttpPost(getFullUrl("/cxs/context.json?sessionId=" + SESSION_ID_1));
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
 
         executeContextJSONRequest(request, SESSION_ID_1);
@@ -148,11 +146,11 @@ public class BasicIT extends BaseIT {
     }
 
     @Test
-    public void testContextJSON() throws IOException, InterruptedException {
+    public void testContextJSON() throws Exception {
         LOGGER.info("Start test testContextJSON");
         ContextRequest contextRequest = new ContextRequest();
         contextRequest.setSessionId(SESSION_ID_2);
-        HttpPost request = new HttpPost(URL + "/cxs/context.json");
+        HttpPost request = new HttpPost(getFullUrl("/cxs/context.json"));
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
 
         executeContextJSONRequest(request, SESSION_ID_2);
@@ -160,7 +158,7 @@ public class BasicIT extends BaseIT {
     }
 
     @Test
-    public void testMultipleLoginOnSameBrowser() throws IOException, InterruptedException {
+    public void testMultipleLoginOnSameBrowser() throws Exception {
         LOGGER.info("Start test testMultipleLoginOnSameBrowser");
 
         // Add login event condition
@@ -180,7 +178,7 @@ public class BasicIT extends BaseIT {
 
         // First page view with the first visitor aka VISITOR_1 and SESSION_ID_3
         ContextRequest contextRequestPageViewSession1 = getContextRequestWithPageViewEvent(sourceSite, SESSION_ID_3);
-        HttpPost requestPageView1 = new HttpPost(URL + "/cxs/context.json");
+        HttpPost requestPageView1 = new HttpPost(getFullUrl("/cxs/context.json"));
         requestPageView1.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession1),
                 ContentType.create("application/json")));
         TestUtils.RequestResponse requestResponsePageView1 = executeContextJSONRequest(requestPageView1, SESSION_ID_3);
@@ -198,7 +196,7 @@ public class BasicIT extends BaseIT {
         // Create login event with VISITOR_1
         ContextRequest contextRequestLoginVisitor1 = getContextRequestWithLoginEvent(sourceSite, loginEventPropertiesVisitor1,
                 EMAIL_VISITOR_1, SESSION_ID_3);
-        HttpPost requestLoginVisitor1 = new HttpPost(URL + "/cxs/context.json");
+        HttpPost requestLoginVisitor1 = new HttpPost(getFullUrl("/cxs/context.json"));
         requestLoginVisitor1.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
         requestLoginVisitor1.addHeader("X-Unomi-Peer", UNOMI_KEY);
         requestLoginVisitor1.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestLoginVisitor1),
@@ -212,7 +210,7 @@ public class BasicIT extends BaseIT {
         Thread.sleep(1000);
 
         // Lets add a page view with VISITOR_1 to simulate reloading the page after login and be able to check the profile properties
-        HttpPost requestPageView2 = new HttpPost(URL + "/cxs/context.json");
+        HttpPost requestPageView2 = new HttpPost(getFullUrl("/cxs/context.json"));
         requestPageView2.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
         requestPageView2.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession1),
                 ContentType.create("application/json")));
@@ -227,7 +225,7 @@ public class BasicIT extends BaseIT {
         // Lets simulate a logout by requesting the context with a new page view event and a new session id
         // but we will send the cookie of the profile id from VISITOR_1
         ContextRequest contextRequestPageViewSession2 = getContextRequestWithPageViewEvent(sourceSite, SESSION_ID_4);
-        HttpPost requestPageView3 = new HttpPost(URL + "/cxs/context.json");
+        HttpPost requestPageView3 = new HttpPost(getFullUrl("/cxs/context.json"));
         requestPageView3.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
         requestPageView3.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession2),
                 ContentType.create("application/json")));
@@ -252,7 +250,7 @@ public class BasicIT extends BaseIT {
         // Create login event with VISITOR_2
         ContextRequest contextRequestLoginVisitor2 = getContextRequestWithLoginEvent(sourceSite, loginEventPropertiesVisitor2,
                 EMAIL_VISITOR_2, SESSION_ID_4);
-        HttpPost requestLoginVisitor2 = new HttpPost(URL + "/cxs/context.json");
+        HttpPost requestLoginVisitor2 = new HttpPost(getFullUrl("/cxs/context.json"));
         requestLoginVisitor2.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
         requestLoginVisitor2.addHeader("X-Unomi-Peer", UNOMI_KEY);
         requestLoginVisitor2.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestLoginVisitor2),
@@ -266,7 +264,7 @@ public class BasicIT extends BaseIT {
         Thread.sleep(1000);
 
         // Lets add a page view with VISITOR_2 to simulate reloading the page after login
-        HttpPost requestPageView4 = new HttpPost(URL + "/cxs/context.json");
+        HttpPost requestPageView4 = new HttpPost(getFullUrl("/cxs/context.json"));
         requestPageView4.addHeader("Cookie", requestResponseLoginVisitor2.getCookieHeaderValue());
         requestPageView4.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession2),
                 ContentType.create("application/json")));
diff --git a/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java b/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
index c9eb4017a..d74672ca3 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
@@ -50,11 +50,6 @@ public class ConditionEvaluatorIT extends BaseIT {
     protected Item emptyItem;
     protected Date lastVisit;
 
-    @Inject @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-    @Inject @Filter(timeout = 600000)
-    private DefinitionsService definitionsService;
-
     protected boolean eval(Condition c) {
         return persistenceService.testMatch(c, item);
     }
diff --git a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
index 830e995e7..75931756b 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -93,34 +93,6 @@ public class ContextServletIT extends BaseIT {
     private static final int DEFAULT_TRYING_TRIES = 30;
     public static final String TEST_SCOPE = "test-scope";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected SegmentService segmentService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected SchemaService schemaService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected ScopeService scopeService;
-
     private Profile profile;
 
     @Before
@@ -179,7 +151,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testUpdateEventFromContextAuthorizedThirdParty_Success() throws IOException, InterruptedException {
+    public void testUpdateEventFromContextAuthorizedThirdParty_Success() throws Exception {
         //Arrange
         String eventId = "test-event-id-" + System.currentTimeMillis();
         String sessionId = "test-session-id";
@@ -205,7 +177,7 @@ public class ContextServletIT extends BaseIT {
         ContextRequest contextRequest = new ContextRequest();
         contextRequest.setSessionId(session.getItemId());
         contextRequest.setEvents(Arrays.asList(event));
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request, sessionId);
@@ -217,7 +189,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testUpdateEventFromContextUnAuthorizedThirdParty_Fail() throws IOException, InterruptedException {
+    public void testUpdateEventFromContextUnAuthorizedThirdParty_Fail() throws Exception {
         //Arrange
         String eventId = "test-event-id-" + System.currentTimeMillis();
         String sessionId = "test-session-id";
@@ -244,7 +216,7 @@ public class ContextServletIT extends BaseIT {
         ContextRequest contextRequest = new ContextRequest();
         contextRequest.setSessionId(session.getItemId());
         contextRequest.setEvents(Arrays.asList(event));
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request, sessionId);
 
@@ -255,7 +227,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testUpdateEventFromContextAuthorizedThirdPartyNoItemID_Fail() throws IOException, InterruptedException {
+    public void testUpdateEventFromContextAuthorizedThirdPartyNoItemID_Fail() throws Exception {
         //Arrange
         String eventId = "test-event-id-" + System.currentTimeMillis();
         String sessionId = "test-session-id";
@@ -276,7 +248,7 @@ public class ContextServletIT extends BaseIT {
         ContextRequest contextRequest = new ContextRequest();
         contextRequest.setSessionId(session.getItemId());
         contextRequest.setEvents(Arrays.asList(event));
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request, sessionId);
 
@@ -288,7 +260,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws IOException, InterruptedException {
+    public void testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws Exception {
         //Arrange
         String sessionId = "test-session-id";
         String scope = TEST_SCOPE;
@@ -301,7 +273,7 @@ public class ContextServletIT extends BaseIT {
         contextRequest.setSessionId(sessionId);
         contextRequest.setRequireSegments(true);
         contextRequest.setEvents(Arrays.asList(event));
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         String cookieHeaderValue = TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
 
@@ -320,14 +292,14 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegment() throws IOException, InterruptedException {
+    public void testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegment() throws Exception {
         //Arrange
         String sessionId = "test-session-id";
         String scope = TEST_SCOPE;
         Event event = new Event();
         event.setEventType(TEST_EVENT_TYPE);
         event.setScope(scope);
-        String regularURI = URL + CONTEXT_URL;
+        String regularURI = getFullUrl(CONTEXT_URL);
         long oldTimestamp = LocalDateTime.now(ZoneId.of("UTC")).minusDays(SEGMENT_NUMBER_OF_DAYS + 1).toInstant(ZoneOffset.UTC)
                 .toEpochMilli();
         String customTimestampURI = regularURI + "?timestamp=" + oldTimestamp;
@@ -353,14 +325,14 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSegment() throws IOException, InterruptedException {
+    public void testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSegment() throws Exception {
         //Arrange
         String sessionId = "test-session-id";
         String scope = TEST_SCOPE;
         Event event = new Event();
         event.setEventType(TEST_EVENT_TYPE);
         event.setScope(scope);
-        String regularURI = URL + CONTEXT_URL;
+        String regularURI = getFullUrl(CONTEXT_URL);
         long futureTimestamp = LocalDateTime.now(ZoneId.of("UTC")).plusDays(1).toInstant(ZoneOffset.UTC).toEpochMilli();
         String customTimestampURI = regularURI + "?timestamp=" + futureTimestamp;
 
@@ -386,7 +358,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testCreateEventWithProfileId_Success() throws IOException, InterruptedException {
+    public void testCreateEventWithProfileId_Success() throws Exception {
         //Arrange
         String eventId = "test-event-id-" + System.currentTimeMillis();
         String eventType = "test-event-type";
@@ -399,7 +371,7 @@ public class ContextServletIT extends BaseIT {
         contextRequest.setEvents(Arrays.asList(event));
 
         //Act
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request);
@@ -409,7 +381,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testCreateEventWithPropertiesValidation_Success() throws IOException, InterruptedException {
+    public void testCreateEventWithPropertiesValidation_Success() throws Exception {
         //Arrange
         String eventId = "valid-event-id-" + System.currentTimeMillis();
         String profileId = "valid-profile-id";
@@ -426,7 +398,7 @@ public class ContextServletIT extends BaseIT {
         contextRequest.setEvents(Arrays.asList(event));
 
         //Act
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request);
@@ -439,7 +411,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testCreateEventWithPropertyValueValidation_Failure() throws IOException, InterruptedException {
+    public void testCreateEventWithPropertyValueValidation_Failure() throws Exception {
         //Arrange
         String eventId = "invalid-event-value-id-" + System.currentTimeMillis();
         String profileId = "invalid-profile-id";
@@ -456,7 +428,7 @@ public class ContextServletIT extends BaseIT {
         contextRequest.setEvents(Arrays.asList(event));
 
         //Act
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request);
@@ -467,7 +439,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testCreateEventWithPropertyNameValidation_Failure() throws IOException, InterruptedException {
+    public void testCreateEventWithPropertyNameValidation_Failure() throws Exception {
         //Arrange
         String eventId = "invalid-event-prop-id-" + System.currentTimeMillis();
         String profileId = "invalid-profile-id";
@@ -483,7 +455,7 @@ public class ContextServletIT extends BaseIT {
         contextRequest.setEvents(Arrays.asList(event));
 
         //Act
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request);
@@ -494,7 +466,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testOGNLVulnerability() throws IOException, InterruptedException {
+    public void testOGNLVulnerability() throws Exception {
 
         File vulnFile = new File("target/vuln-file.txt");
         if (vulnFile.exists()) {
@@ -505,7 +477,7 @@ public class ContextServletIT extends BaseIT {
 
         Map<String, String> parameters = new HashMap<>();
         parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(
                 new StringEntity(getValidatedBundleJSON("security/ognl-payload-1.json", parameters), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request);
@@ -515,7 +487,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testMVELVulnerability() throws IOException, InterruptedException {
+    public void testMVELVulnerability() throws Exception {
 
         File vulnFile = new File("target/vuln-file.txt");
         if (vulnFile.exists()) {
@@ -526,7 +498,7 @@ public class ContextServletIT extends BaseIT {
 
         Map<String, String> parameters = new HashMap<>();
         parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(
                 new StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json", parameters), ContentType.APPLICATION_JSON));
         TestUtils.executeContextJSONRequest(request);
@@ -536,21 +508,21 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testPersonalization() throws IOException, InterruptedException {
+    public void testPersonalization() throws Exception {
 
         Map<String, String> parameters = new HashMap<>();
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(new StringEntity(getValidatedBundleJSON("personalization.json", parameters), ContentType.APPLICATION_JSON));
         TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
         assertEquals("Invalid response code", 200, response.getStatusCode());
     }
 
     @Test
-    public void testPersonalizationWithControlGroup() throws IOException, InterruptedException {
+    public void testPersonalizationWithControlGroup() throws Exception {
 
         Map<String, String> parameters = new HashMap<>();
         parameters.put("storeInSession", "false");
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(
                 new StringEntity(getValidatedBundleJSON("personalization-controlgroup.json", parameters), ContentType.APPLICATION_JSON));
         TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
@@ -576,7 +548,7 @@ public class ContextServletIT extends BaseIT {
 
         // now let's test with session storage
         parameters.put("storeInSession", "true");
-        request = new HttpPost(URL + CONTEXT_URL);
+        request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(
                 new StringEntity(getValidatedBundleJSON("personalization-controlgroup.json", parameters), ContentType.APPLICATION_JSON));
         response = TestUtils.executeContextJSONRequest(request);
@@ -627,7 +599,7 @@ public class ContextServletIT extends BaseIT {
     }
 
     @Test
-    public void testRequireScoring() throws IOException, InterruptedException {
+    public void testRequireScoring() throws Exception {
 
         Map<String, String> parameters = new HashMap<>();
         String scoringSource = getValidatedBundleJSON("score1.json", parameters);
@@ -639,7 +611,7 @@ public class ContextServletIT extends BaseIT {
 
         // first let's make sure everything works without the requireScoring parameter
         parameters = new HashMap<>();
-        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(new StringEntity(getValidatedBundleJSON("withoutRequireScores.json", parameters), ContentType.APPLICATION_JSON));
         TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
         assertEquals("Invalid response code", 200, response.getStatusCode());
@@ -650,7 +622,7 @@ public class ContextServletIT extends BaseIT {
 
         // now let's test adding it.
         parameters = new HashMap<>();
-        request = new HttpPost(URL + CONTEXT_URL);
+        request = new HttpPost(getFullUrl(CONTEXT_URL));
         request.setEntity(new StringEntity(getValidatedBundleJSON("withRequireScores.json", parameters), ContentType.APPLICATION_JSON));
         response = TestUtils.executeContextJSONRequest(request);
         assertEquals("Invalid response code", 200, response.getStatusCode());
diff --git a/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java b/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java
index f74b81223..edbca99ab 100644
--- a/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java
@@ -64,13 +64,6 @@ public class CopyPropertiesActionIT extends BaseIT {
     public static final String PROPERTY_TO_MAP = "PropertyToMap";
     public static final String MAPPED_PROPERTY = "MappedProperty";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
     @Before
     public void setUp() throws InterruptedException {
         Profile profile = new Profile();
diff --git a/itests/src/test/java/org/apache/unomi/itests/EventServiceIT.java b/itests/src/test/java/org/apache/unomi/itests/EventServiceIT.java
index 06622f50d..4bde047da 100644
--- a/itests/src/test/java/org/apache/unomi/itests/EventServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/EventServiceIT.java
@@ -49,14 +49,6 @@ public class EventServiceIT extends BaseIT {
 
     private final static String TEST_PROFILE_ID = "test-profile-id";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @After
     public void tearDown() {
         TestUtils.removeAllEvents(definitionsService, persistenceService);
diff --git a/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java b/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
index 9c38e7642..20c507f8d 100644
--- a/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
@@ -55,22 +55,6 @@ public class GroovyActionsServiceIT extends BaseIT {
     public static final String UPDATE_ADDRESS_ACTION_GROOVY_FILE = "data/tmp/groovy/UpdateAddressAction.groovy";
     public static final String UPDATE_ADDRESS_ACTION = "UpdateAddressAction";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected GroovyActionsService groovyActionsService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
     @Before
     public void setUp() throws InterruptedException {
         Profile profile = new Profile();
diff --git a/itests/src/test/java/org/apache/unomi/itests/IncrementPropertyIT.java b/itests/src/test/java/org/apache/unomi/itests/IncrementPropertyIT.java
index 985aed06a..98fe21717 100644
--- a/itests/src/test/java/org/apache/unomi/itests/IncrementPropertyIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/IncrementPropertyIT.java
@@ -55,22 +55,6 @@ public class IncrementPropertyIT extends BaseIT {
     private Rule rule;
     private Event event;
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected RulesService rulesService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
     @Before
     public void setup() throws Exception {
         profile = createProfile();
diff --git a/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java b/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
index 3ca188796..8248ca3c7 100644
--- a/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
@@ -61,14 +61,6 @@ public class InputValidationIT extends BaseIT {
     private final static String ERROR_MESSAGE_INVALID_DATA_RECEIVED = "Request rejected by the server because: Invalid received data";
     public static final String DUMMY_SCOPE = "dummy_scope";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected SchemaService schemaService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected ScopeService scopeService;
-
     @Before
     public void setUp() throws InterruptedException {
         TestUtils.createScope(DUMMY_SCOPE, "Dummy scope", scopeService);
@@ -82,25 +74,25 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_param_EventsCollectorRequestNotNull() throws IOException {
+    public void test_param_EventsCollectorRequestNotNull() throws Exception {
         doPOSTRequestTest(EVENT_COLLECTOR_URL, null, null, 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
         doGETRequestTest(EVENT_COLLECTOR_URL, null, null, 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
     }
 
     @Test
-    public void test_param_EventsNotEmpty() throws IOException {
+    public void test_param_EventsNotEmpty() throws Exception {
         doPOSTRequestTest(EVENT_COLLECTOR_URL, null, "/validation/eventcollector_emptyEvents.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
         doGETRequestTest(EVENT_COLLECTOR_URL, null, "/validation/eventcollector_emptyEvents.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
     }
 
     @Test
-    public void test_param_SessionIDPattern() throws IOException {
+    public void test_param_SessionIDPattern() throws Exception {
         doPOSTRequestTest(EVENT_COLLECTOR_URL, null, "/validation/eventcollector_invalidSessionId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
         doGETRequestTest(EVENT_COLLECTOR_URL, null, "/validation/eventcollector_invalidSessionId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
     }
 
     @Test
-    public void test_eventCollector_valid() throws IOException, InterruptedException {
+    public void test_eventCollector_valid() throws Exception {
         // needed schema for event to be valid during tests
         schemaService.saveSchema(resourceAsString("schemas/schema-dummy.json"));
         schemaService.saveSchema(resourceAsString("schemas/schema-dummy-properties.json"));
@@ -122,7 +114,7 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_contextRequest_SessionIDPattern() throws IOException {
+    public void test_contextRequest_SessionIDPattern() throws Exception {
         doPOSTRequestTest(CONTEXT_JSON_URL, null, "/validation/contextRequest_invalidSessionId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
         doPOSTRequestTest(CONTEXT_JS_URL, null, "/validation/contextRequest_invalidSessionId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
         doGETRequestTest(CONTEXT_JSON_URL, null, "/validation/contextRequest_invalidSessionId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
@@ -130,7 +122,7 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_contextRequest_ProfileIDPattern() throws IOException {
+    public void test_contextRequest_ProfileIDPattern() throws Exception {
         doPOSTRequestTest(CONTEXT_JSON_URL, null, "/validation/contextRequest_invalidProfileId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
         doPOSTRequestTest(CONTEXT_JS_URL, null, "/validation/contextRequest_invalidProfileId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
         doGETRequestTest(CONTEXT_JSON_URL, null, "/validation/contextRequest_invalidProfileId.json", 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
@@ -138,7 +130,7 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_contextRequest_valid() throws IOException {
+    public void test_contextRequest_valid() throws Exception {
         doPOSTRequestTest(CONTEXT_JSON_URL, null, "/validation/contextRequest_valid.json", 200, null);
         doPOSTRequestTest(CONTEXT_JS_URL, null, "/validation/contextRequest_valid.json", 200, null);
         doGETRequestTest(CONTEXT_JSON_URL, null, "/validation/contextRequest_valid.json", 200, null);
@@ -146,7 +138,7 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_eventCollector_request_size_exceed_limit() throws IOException, InterruptedException {
+    public void test_eventCollector_request_size_exceed_limit() throws Exception {
         // needed schema for event to be valid during tests
         schemaService.saveSchema(resourceAsString("schemas/schema-dummy.json"));
         schemaService.saveSchema(resourceAsString("schemas/schema-dummy-properties.json"));
@@ -168,7 +160,7 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_contextJSON_SessionIDPattern() throws IOException {
+    public void test_contextJSON_SessionIDPattern() throws Exception {
         String baseUrl = CONTEXT_JS_URL;
         String queryString = "?sessionId=" + URLEncoder.encode("<script>alert();</script>", StandardCharsets.UTF_8.toString());
         doPOSTRequestTest(baseUrl + queryString, null, null, 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
@@ -189,7 +181,7 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_contextJSON_PersonaIdPattern() throws IOException {
+    public void test_contextJSON_PersonaIdPattern() throws Exception {
         String baseUrl = CONTEXT_JS_URL;
         String queryString = "?personaId=" + URLEncoder.encode("<script>alert();</script>", StandardCharsets.UTF_8.toString());
         doPOSTRequestTest(baseUrl + queryString, null, null, 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
@@ -210,7 +202,7 @@ public class InputValidationIT extends BaseIT {
     }
 
     @Test
-    public void test_cookie_profileIdPattern() throws IOException {
+    public void test_cookie_profileIdPattern() throws Exception {
         Map<String, String> headers = new HashMap<>();
         headers.put("Cookie", "context-profile-id=<script>alert();</script>");
         doPOSTRequestTest(CONTEXT_JSON_URL, headers, null, 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
@@ -225,11 +217,11 @@ public class InputValidationIT extends BaseIT {
         doGETRequestTest(CONTEXT_JS_URL, headers, null, 200, null);
     }
 
-    private void doGETRequestTest(String uri, Map<String, String> headers, String entityResourcePath, int expectedHTTPStatusCode, String expectedErrorMessage) throws IOException {
+    private void doGETRequestTest(String uri, Map<String, String> headers, String entityResourcePath, int expectedHTTPStatusCode, String expectedErrorMessage) throws Exception {
         // test old servlets
-        performGETRequestTest(URL + uri, headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
+        performGETRequestTest(getFullUrl(uri), headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
         // test directly CXS endpoints
-        performGETRequestTest(URL + "/cxs" + uri, headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
+        performGETRequestTest(getFullUrl("/cxs" + uri), headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
     }
 
     private void performGETRequestTest(String url, Map<String, String> headers, String entityResourcePath, int expectedHTTPStatusCode, String expectedErrorMessage) throws IOException {
@@ -240,11 +232,11 @@ public class InputValidationIT extends BaseIT {
         performRequest(new HttpGet(url), headers, expectedHTTPStatusCode, expectedErrorMessage);
     }
 
-    private void doPOSTRequestTest(String uri, Map<String, String> headers, String entityResourcePath, int expectedHTTPStatusCode, String expectedErrorMessage) throws IOException {
+    private void doPOSTRequestTest(String uri, Map<String, String> headers, String entityResourcePath, int expectedHTTPStatusCode, String expectedErrorMessage) throws Exception {
         // test old servlets
-        performPOSTRequestTest(URL + uri, headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
+        performPOSTRequestTest(getFullUrl(uri), headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
         // test directly CXS endpoints
-        performPOSTRequestTest(URL + "/cxs" + uri, headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
+        performPOSTRequestTest(getFullUrl("/cxs" + uri), headers, entityResourcePath, expectedHTTPStatusCode, expectedErrorMessage);
     }
 
     private void performPOSTRequestTest(String url, Map<String, String> headers, String entityResourcePath, int expectedHTTPStatusCode, String expectedErrorMessage) throws IOException {
diff --git a/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java b/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
index 75f7d1310..f30e3f1a6 100644
--- a/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
@@ -61,14 +61,6 @@ public class JSONSchemaIT extends BaseIT {
     private static final int DEFAULT_TRYING_TRIES = 30;
     public static final String DUMMY_SCOPE = "dummy_scope";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected SchemaService schemaService;
-
-    @Inject
-    @Filter(timeout = 6000000)
-    protected ScopeService scopeService;
-
     @Before
     public void setUp() throws InterruptedException {
         keepTrying("Couldn't find json schema endpoint", () -> get(JSONSCHEMA_URL, List.class), Objects::nonNull, DEFAULT_TRYING_TIMEOUT,
@@ -329,7 +321,7 @@ public class JSONSchemaIT extends BaseIT {
         }
     }
 
-    private Event sendEventAndWaitItsIndexed(String eventResourcePath) throws IOException, InterruptedException {
+    private Event sendEventAndWaitItsIndexed(String eventResourcePath) throws Exception {
         // build event collector request
         String eventMarker = UUID.randomUUID().toString();
         HashMap<String, String> eventReplacements = new HashMap<>();
@@ -353,8 +345,8 @@ public class JSONSchemaIT extends BaseIT {
         return events.get(0);
     }
 
-    private void eventCollectorPost(String eventCollectorRequest) {
-        HttpPost request = new HttpPost(URL + EVENT_COLLECTOR_URL);
+    private void eventCollectorPost(String eventCollectorRequest) throws Exception {
+        HttpPost request = new HttpPost(getFullUrl(EVENT_COLLECTOR_URL));
         request.addHeader("Content-Type", "application/json");
         request.setEntity(new StringEntity(eventCollectorRequest, ContentType.create("application/json")));
         CloseableHttpResponse response;
diff --git a/itests/src/test/java/org/apache/unomi/itests/ModifyConsentIT.java b/itests/src/test/java/org/apache/unomi/itests/ModifyConsentIT.java
index 1deb98cbe..365da464e 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ModifyConsentIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ModifyConsentIT.java
@@ -48,14 +48,6 @@ public class ModifyConsentIT extends BaseIT {
 
     private final static String PROFILE_TEST_ID = "profile-consent";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
     @Before
     public void setUp() throws InterruptedException {
         Profile profile = new Profile();
diff --git a/itests/src/test/java/org/apache/unomi/itests/PatchIT.java b/itests/src/test/java/org/apache/unomi/itests/PatchIT.java
index 181a2eb28..505837175 100644
--- a/itests/src/test/java/org/apache/unomi/itests/PatchIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/PatchIT.java
@@ -44,25 +44,6 @@ import java.io.IOException;
 public class PatchIT extends BaseIT {
     private Logger logger = LoggerFactory.getLogger(PatchIT.class);
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected PatchService patchService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
-    @Inject
-    protected BundleContext bundleContext;
-
     @Test
     public void testPatch() throws IOException {
         PropertyType company = profileService.getPropertyType("company");
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileExportIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileExportIT.java
index 82733531c..daa353fa2 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileExportIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileExportIT.java
@@ -49,13 +49,6 @@ import java.util.UUID;
 public class ProfileExportIT extends BaseIT {
     private Logger logger = LoggerFactory.getLogger(ProfileExportIT.class);
 
-    @Inject
-    @Filter(value = "(configDiscriminator=EXPORT)", timeout = 600000)
-    protected ImportExportConfigurationService<ExportConfiguration> exportConfigurationService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Test
     public void testExport() throws InterruptedException {
         Date timestamp = new Date();
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileImportActorsIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileImportActorsIT.java
index bfab6a3cf..f42fbe9ab 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileImportActorsIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileImportActorsIT.java
@@ -48,13 +48,6 @@ import java.util.Objects;
 @ExamReactorStrategy(PerSuite.class)
 public class ProfileImportActorsIT extends BaseIT {
 
-    @Inject
-    @Filter(value = "(configDiscriminator=IMPORT)", timeout = 600000)
-    protected ImportExportConfigurationService<ImportConfiguration> importConfigurationService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Test
     public void testImportActors() throws InterruptedException {
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileImportBasicIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileImportBasicIT.java
index 7f7c7c2a1..cfce7009c 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileImportBasicIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileImportBasicIT.java
@@ -49,11 +49,6 @@ import java.util.Map;
 public class ProfileImportBasicIT extends BaseIT {
     private Logger logger = LoggerFactory.getLogger(ProfileImportBasicIT.class);
 
-    @Inject @Filter(value="(configDiscriminator=IMPORT)", timeout = 600000)
-    protected ImportExportConfigurationService<ImportConfiguration> importConfigurationService;
-    @Inject @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Test
     public void testImportBasic() throws IOException, InterruptedException {
         /*** Basic Test ***/
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileImportRankingIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileImportRankingIT.java
index 12737e217..2b321b26e 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileImportRankingIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileImportRankingIT.java
@@ -47,13 +47,6 @@ import java.util.Objects;
 @ExamReactorStrategy(PerSuite.class)
 public class ProfileImportRankingIT extends BaseIT {
 
-    @Inject
-    @Filter(value = "(configDiscriminator=IMPORT)", timeout = 600000)
-    protected ImportExportConfigurationService<ImportConfiguration> importConfigurationService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Test
     public void testImportRanking() throws InterruptedException {
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileImportSurfersIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileImportSurfersIT.java
index 4612b2058..e1d478bea 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileImportSurfersIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileImportSurfersIT.java
@@ -50,13 +50,6 @@ import java.util.Objects;
 public class ProfileImportSurfersIT extends BaseIT {
     private Logger logger = LoggerFactory.getLogger(ProfileImportSurfersIT.class);
 
-    @Inject
-    @Filter(value = "(configDiscriminator=IMPORT)", timeout = 600000)
-    protected ImportExportConfigurationService<ImportConfiguration> importConfigurationService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Test
     public void testImportSurfers() throws InterruptedException {
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileMergeIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileMergeIT.java
index ca260ed11..206b21600 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileMergeIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileMergeIT.java
@@ -51,22 +51,6 @@ import java.util.Objects;
 @ExamReactorStrategy(PerSuite.class)
 public class ProfileMergeIT extends BaseIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected RulesService rulesService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
     private final static String TEST_EVENT_TYPE = "mergeProfileTestEventType";
     private final static String TEST_RULE_ID = "mergeOnPropertyTest";
     private final static String TEST_PROFILE_ID = "mergeOnPropertyTestProfileId";
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileServiceIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileServiceIT.java
index 1340816de..8e38424e8 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileServiceIT.java
@@ -58,18 +58,6 @@ public class ProfileServiceIT extends BaseIT {
 
     private static final String TEST_PROFILE_ALIAS = "test-profile-alias";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
     @Before
     public void setUp() {
         TestUtils.removeAllProfiles(definitionsService, persistenceService);
diff --git a/itests/src/test/java/org/apache/unomi/itests/ProfileServiceWithoutOverwriteIT.java b/itests/src/test/java/org/apache/unomi/itests/ProfileServiceWithoutOverwriteIT.java
index aa3a34c2d..79e733ca5 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ProfileServiceWithoutOverwriteIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ProfileServiceWithoutOverwriteIT.java
@@ -49,7 +49,7 @@ public class ProfileServiceWithoutOverwriteIT extends BaseIT {
     private final static String TEST_PROFILE_ID = "test-profile-id";
 
     @Configuration
-    public Option[] config() throws InterruptedException {
+    public Option[] config() {
         List<Option> options = new ArrayList<>();
         options.addAll(Arrays.asList(super.config()));
         options.add(systemProperty("org.apache.unomi.elasticsearch.throwExceptions").value("true"));
@@ -57,18 +57,6 @@ public class ProfileServiceWithoutOverwriteIT extends BaseIT {
         return options.toArray(new Option[0]);
     }
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
     @Before
     public void setUp() {
         TestUtils.removeAllProfiles(definitionsService, persistenceService);
diff --git a/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java b/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java
index 4628332e2..21d3f9290 100644
--- a/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java
@@ -61,16 +61,6 @@ public class PropertiesUpdateActionIT extends BaseIT {
     private final static String PROFILE_TARGET_TEST_ID = "profile-target-event";
     private final static String PROFILE_TEST_ID = "profile-to-update-by-event";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected RulesService rulesService;
-
     @Before
     public void setUp() throws InterruptedException {
         Profile profile = new Profile();
diff --git a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
index 2943d073d..aa88cdeb0 100644
--- a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
@@ -55,14 +55,6 @@ public class RuleServiceIT extends BaseIT {
     private final static String TEST_RULE_ID = "test-rule-id";
     public static final String TEST_SCOPE = "test-scope";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected RulesService rulesService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
     @Before
     public void setUp() {
         TestUtils.removeAllProfiles(definitionsService, persistenceService);
diff --git a/itests/src/test/java/org/apache/unomi/itests/ScopeIT.java b/itests/src/test/java/org/apache/unomi/itests/ScopeIT.java
index 39f242f79..11edf0d67 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ScopeIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ScopeIT.java
@@ -50,14 +50,6 @@ public class ScopeIT extends BaseIT {
     private static final int DEFAULT_TRYING_TIMEOUT = 2000;
     private static final int DEFAULT_TRYING_TRIES = 30;
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ScopeService scopeService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
     @Before
     public void setUp() throws InterruptedException {
         keepTrying("Couldn't find scope endpoint", () -> get(SCOPE_URL, List.class), Objects::nonNull, DEFAULT_TRYING_TIMEOUT,
diff --git a/itests/src/test/java/org/apache/unomi/itests/SecurityIT.java b/itests/src/test/java/org/apache/unomi/itests/SecurityIT.java
index f76c61231..69f64d6d8 100644
--- a/itests/src/test/java/org/apache/unomi/itests/SecurityIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/SecurityIT.java
@@ -54,7 +54,7 @@ public class SecurityIT extends BaseIT {
     }
 
     @Test
-    public void testOGNLInjection() throws IOException {
+    public void testOGNLInjection() throws Exception {
         ContextRequest contextRequest = new ContextRequest();
         List<PersonalizationService.PersonalizationRequest> personalizations = new ArrayList<>();
         PersonalizationService.PersonalizationRequest personalizationRequest = new PersonalizationService.PersonalizationRequest();
@@ -92,7 +92,7 @@ public class SecurityIT extends BaseIT {
         contextRequest.setPersonalizations(personalizations);
 
         contextRequest.setSessionId(SESSION_ID);
-        HttpPost request = new HttpPost(URL + "/cxs/context.json");
+        HttpPost request = new HttpPost(getFullUrl("/cxs/context.json"));
         request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
 
         TestUtils.RequestResponse response = executeContextJSONRequest(request, SESSION_ID);
diff --git a/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java b/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
index 1eb03718a..b1a3c0626 100644
--- a/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
@@ -58,22 +58,6 @@ public class SegmentIT extends BaseIT {
     private final static Logger LOGGER = LoggerFactory.getLogger(SegmentIT.class);
     private final static String SEGMENT_ID = "test-segment-id-2";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected SegmentService segmentService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
     @Before
     public void setUp() throws InterruptedException {
         removeItems(Segment.class);
diff --git a/itests/src/test/java/org/apache/unomi/itests/SendEventActionIT.java b/itests/src/test/java/org/apache/unomi/itests/SendEventActionIT.java
index bae27b344..3b77dae37 100644
--- a/itests/src/test/java/org/apache/unomi/itests/SendEventActionIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/SendEventActionIT.java
@@ -48,13 +48,6 @@ public class SendEventActionIT extends BaseIT {
     private final static String TEST_EVENT_TYPE = "sendEventTestEventType";
     private final static String TEST_PROFILE_ID = "sendEventTestProfileId";
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
     @After
     public void tearDown() throws InterruptedException {
         eventService.removeProfileEvents(TEST_PROFILE_ID);
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/BaseGraphQLIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/BaseGraphQLIT.java
index 9b8ff09cf..3f6ab35d8 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/BaseGraphQLIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/BaseGraphQLIT.java
@@ -44,25 +44,20 @@ import java.util.regex.Pattern;
 @ExamReactorStrategy(PerSuite.class)
 public abstract class BaseGraphQLIT extends BaseIT {
 
-    protected static final String GRAPHQL_ENDPOINT = URL + "/graphql";
-
     protected static final ContentType JSON_CONTENT_TYPE = ContentType.create("application/json");
 
-    @Inject
-    protected BundleContext bundleContext;
-
-    protected CloseableHttpResponse postAnonymous(final String resource) throws IOException {
+    protected CloseableHttpResponse postAnonymous(final String resource) throws Exception {
         return postAs(resource, null, null);
     }
 
-    protected CloseableHttpResponse post(final String resource) throws IOException {
+    protected CloseableHttpResponse post(final String resource) throws Exception {
         return postAs(resource, "karaf", "karaf");
     }
 
-    protected CloseableHttpResponse postAs(final String resource, final String username, final String password) throws IOException {
+    protected CloseableHttpResponse postAs(final String resource, final String username, final String password) throws Exception {
         final String resourceAsString = resourceAsString(resource);
 
-        final HttpPost request = new HttpPost(GRAPHQL_ENDPOINT);
+        final HttpPost request = new HttpPost(getFullUrl("/graphql"));
 
         request.setEntity(new StringEntity(resourceAsString, JSON_CONTENT_TYPE));
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java
index 5c9e02d0c..385a4ff57 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java
@@ -35,18 +35,6 @@ import java.util.Map;
 
 public class GraphQLEventIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected EventService eventService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected PersistenceService persistenceService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
     private final String profileID = "profile-1";
     private final String eventID = "event-1";
     private Profile profile;
@@ -61,7 +49,7 @@ public class GraphQLEventIT extends BaseGraphQLIT {
 
 
     @Test
-    public void testGetEvent_notExists() throws IOException {
+    public void testGetEvent_notExists() throws Exception {
         try (CloseableHttpResponse response = post("graphql/event/get-event-not-exists.json")) {
             final ResponseContext context = ResponseContext.parse(response.getEntity());
 
@@ -70,7 +58,7 @@ public class GraphQLEventIT extends BaseGraphQLIT {
     }
 
     @Test
-    public void testGetEvent() throws IOException, InterruptedException {
+    public void testGetEvent() throws Exception {
         final Event event = createEvent(eventID, profile);
         refreshPersistence();
 
@@ -84,7 +72,7 @@ public class GraphQLEventIT extends BaseGraphQLIT {
     }
 
     @Test
-    public void testFindEvents() throws IOException, InterruptedException {
+    public void testFindEvents() throws Exception {
         createEvent(eventID, profile);
         createEvent("event-2", profile);
         final Profile profile2 = new Profile("profile-2");
@@ -102,7 +90,7 @@ public class GraphQLEventIT extends BaseGraphQLIT {
     }
 
     @Test
-    public void testProcessEvents() throws IOException {
+    public void testProcessEvents() throws Exception {
         final Profile originalProfile = persistenceService.load(profileID, Profile.class);
         Assert.assertNull(originalProfile.getProperty("firstName"));
         Assert.assertNull(originalProfile.getProperty("lastName"));
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java
index 326d98f35..712ce9037 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLListIT.java
@@ -28,10 +28,6 @@ import java.util.Objects;
 
 public class GraphQLListIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Test
     public void testCRUD() throws Exception {
         Profile persistedProfile = null;
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileAliasesIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileAliasesIT.java
index 6cd62759e..d7defef44 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileAliasesIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileAliasesIT.java
@@ -29,10 +29,6 @@ import javax.inject.Inject;
 
 public class GraphQLProfileAliasesIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Before
     public void setUp() throws InterruptedException {
         removeItems(ProfileAlias.class);
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileIT.java
index 867b6543e..5914ac294 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfileIT.java
@@ -30,17 +30,13 @@ import java.util.Objects;
 
 public class GraphQLProfileIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Before
     public void setUp() throws InterruptedException {
         removeItems(Profile.class);
     }
 
     @Test
-    public void testGetProfile_WithoutCreation() throws IOException {
+    public void testGetProfile_WithoutCreation() throws Exception {
         try (CloseableHttpResponse response = post("graphql/profile/get-profile-without-creation.json")) {
             final ResponseContext context = ResponseContext.parse(response.getEntity());
 
@@ -49,7 +45,7 @@ public class GraphQLProfileIT extends BaseGraphQLIT {
     }
 
     @Test
-    public void testGetProfile_WithCreation() throws IOException {
+    public void testGetProfile_WithCreation() throws Exception {
         try (CloseableHttpResponse response = post("graphql/profile/get-profile-with-creation.json")) {
             final ResponseContext context = ResponseContext.parse(response.getEntity());
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfilePropertiesIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfilePropertiesIT.java
index a10853e8d..ea9061443 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfilePropertiesIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLProfilePropertiesIT.java
@@ -39,10 +39,6 @@ import java.util.Objects;
 
 public class GraphQLProfilePropertiesIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     private final static Logger LOGGER = LoggerFactory.getLogger(GraphQLProfilePropertiesIT.class);
 
     @Test
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSegmentIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSegmentIT.java
index bed6367b4..2636733c5 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSegmentIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSegmentIT.java
@@ -33,10 +33,6 @@ import java.util.UUID;
 
 public class GraphQLSegmentIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected ProfileService profileService;
-
     @Before
     public void setUp() throws InterruptedException {
         removeItems(Segment.class);
@@ -48,7 +44,7 @@ public class GraphQLSegmentIT extends BaseGraphQLIT {
     }
 
     @Test
-    public void testCreateThenGetAndDeleteSegment() throws IOException, InterruptedException {
+    public void testCreateThenGetAndDeleteSegment() throws Exception {
         try (CloseableHttpResponse response = post("graphql/segment/create-or-update-segment.json")) {
             final ResponseContext context = ResponseContext.parse(response.getEntity());
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSourceIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSourceIT.java
index a5a828fb3..985ad267d 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSourceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLSourceIT.java
@@ -28,12 +28,8 @@ import static org.junit.Assert.*;
 
 public class GraphQLSourceIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    ScopeService scopeService;
-
     @Test
-    public void testCRUD() throws IOException, InterruptedException {
+    public void testCRUD() throws Exception {
         try (CloseableHttpResponse response = post("graphql/source/create-source.json")) {
             final ResponseContext context = ResponseContext.parse(response.getEntity());
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLTopicIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLTopicIT.java
index 96274c4b7..69c14394a 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLTopicIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLTopicIT.java
@@ -27,12 +27,8 @@ import java.io.IOException;
 
 public class GraphQLTopicIT extends BaseGraphQLIT {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected TopicService topicService;
-
     @Test
-    public void testCRUD() throws IOException, InterruptedException {
+    public void testCRUD() throws Exception {
         try (CloseableHttpResponse response = post("graphql/topic/create-topic.json")) {
             final ResponseContext context = ResponseContext.parse(response.getEntity());
 
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLViewIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLViewIT.java
index 5c12b8373..6065b8b29 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLViewIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLViewIT.java
@@ -40,22 +40,6 @@ public class GraphQLViewIT
     extends BaseGraphQLIT
 {
 
-    @Inject
-    @Filter(timeout = 600000)
-    protected UserListService userListService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected TopicService topicService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected SegmentService segmentService;
-
-    @Inject
-    @Filter(timeout = 600000)
-    protected DefinitionsService definitionsService;
-
     @Test
     public void test()
         throws Exception
diff --git a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLWebSocketIT.java b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLWebSocketIT.java
index 74a4ad8a4..bcef7ebc0 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLWebSocketIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLWebSocketIT.java
@@ -42,8 +42,6 @@ public class GraphQLWebSocketIT extends BaseGraphQLIT {
 
     private final static Logger LOGGER = LoggerFactory.getLogger(GraphQLWebSocketIT.class);
 
-    private static final String SUBSCRIPTION_ENDPOINT = "ws://localhost:" + HTTP_PORT + "/graphql";
-
     @Test
     public void testWebSocketConnectionSegment() throws Exception {
         WebSocketClient client = new WebSocketClient();
@@ -52,7 +50,7 @@ public class GraphQLWebSocketIT extends BaseGraphQLIT {
             LOGGER.info("Starting web socket client...");
             client.start();
 
-            URI echoUri = new URI(SUBSCRIPTION_ENDPOINT);
+            URI echoUri = new URI("ws://localhost:" + getHttpPort() + "/graphql");
             ClientUpgradeRequest request = new ClientUpgradeRequest();
 
             Future<Session> onConnected = client.connect(socket, echoUri, request);
diff --git a/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xTo200IT.java b/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xTo200IT.java
new file mode 100644
index 000000000..1372ba2d7
--- /dev/null
+++ b/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xTo200IT.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.itests.migration;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.unomi.api.*;
+import org.apache.unomi.itests.BaseIT;
+import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
+import org.apache.unomi.shell.migration.utils.HttpUtils;
+import org.apache.unomi.shell.migration.utils.MigrationUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class Migrate16xTo200IT extends BaseIT {
+
+    @Override
+    @Before
+    public void waitForStartup() throws InterruptedException {
+
+        // Restore snapshot from 1.6.x
+        try (CloseableHttpClient httpClient = HttpUtils.initHttpClient(true)) {
+            // Create snapshot repo
+            HttpUtils.executePutRequest(httpClient, "http://localhost:9400/_snapshot/snapshots_repository/", resourceAsString("migration/create_snapshots_repository.json"), null);
+            // Get snapshot, insure it exists
+            String snapshot = HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/_snapshot/snapshots_repository/snapshot_1.6.x", null);
+            if (snapshot == null || !snapshot.contains("snapshot_1.6.x")) {
+                throw new RuntimeException("Unable to retrieve 1.6.x snapshot for ES restore");
+            }
+            // Restore the snapshot
+            HttpUtils.executePostRequest(httpClient, "http://localhost:9400/_snapshot/snapshots_repository/snapshot_1.6.x/_restore?wait_for_completion=true", "{}",  null);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        // Do migrate the data set
+        executeCommand("unomi:migrate 1.6.0 true");
+        // Call super for starting Unomi and wait for the complete startup
+        super.waitForStartup();
+    }
+
+    @After
+    public void cleanup() throws InterruptedException {
+        removeItems(Profile.class);
+        removeItems(Session.class);
+        removeItems(Event.class);
+        removeItems(Scope.class);
+    }
+
+    @Test
+    public void checkMigratedData() throws Exception {
+        checkProfileInterests();
+        checkScopeHaveBeenCreated();
+        checkFormEventRestructured();
+        checkViewEventRestructured();
+        checkEventTypesNotPersistedAnymore();
+        checkForMappingUpdates();
+    }
+
+    /**
+     * Multiple index mappings have been update, check a simple check that after migration those mappings contains the latest modifications.
+     */
+    private void checkForMappingUpdates() throws IOException {
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-scope/_mapping", null).contains("\"match\":\"*\",\"match_mapping_type\":\"string\",\"mapping\":{\"analyzer\":\"folding\""));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-segment/_mapping", null).contains("\"condition\":{\"type\":\"object\",\"enabled\":false}"));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-scoring/_mapping", null).contains("\"condition\":{\"type\":\"object\",\"enabled\":false}"));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-campaign/_mapping", null).contains("\"entryCondition\":{\"type\":\"object\",\"enabled\":false}"));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-conditiontype/_mapping", null).contains("\"parentCondition\":{\"type\":\"object\",\"enabled\":false}"));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-goal/_mapping", null).contains("\"startEvent\":{\"type\":\"object\",\"enabled\":false}"));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-patch/_mapping", null).contains("\"data\":{\"type\":\"object\",\"enabled\":false}"));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-rule/_mapping", null).contains("\"condition\":{\"type\":\"object\",\"enabled\":false}"));
+        Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/context-profile/_mapping", null).contains("\"interests\":{\"type\":\"nested\""));
+        for (String eventIndex : MigrationUtils.getIndexesPrefixedBy(httpClient, "http://localhost:9400", "context-event-")) {
+            Assert.assertTrue(HttpUtils.executeGetRequest(httpClient, "http://localhost:9400/" + eventIndex + "/_mapping", null).contains("\"flattenedProperties\":{\"type\":\"flattened\"}"));
+        }
+    }
+
+    /**
+     * Data set contains a form event (id: 7b55b4fd-5ff0-4a85-9dc4-ffde322a1de6) with this data:
+     * {
+     *   "properties": {
+     *     "pets": "cat",
+     *     "firstname": "foo",
+     *     "sports": [
+     *       "football",
+     *       "tennis"
+     *     ],
+     *     "city": "Berlin",
+     *     "age": "15",
+     *     "email": "foo@bar.fr",
+     *     "drone": "dewey",
+     *     "lastname": "bar",
+     *     "contactMethod": [
+     *       "postalMethod",
+     *       "phoneMethod"
+     *     ]
+     *   }
+     * }
+     */
+    private void checkFormEventRestructured() {
+        List<Event> events = persistenceService.query("eventType", "form", null, Event.class);
+        for (Event formEvent : events) {
+            Assert.assertEquals(0, formEvent.getProperties().size());
+            Map<String, Object> fields = (Map<String, Object>) formEvent.getFlattenedProperties().get("fields");
+            Assert.assertTrue(fields.size() > 0);
+
+            if (Objects.equals(formEvent.getItemId(), "7b55b4fd-5ff0-4a85-9dc4-ffde322a1de6")) {
+                // check singled valued
+                Assert.assertEquals("cat", fields.get("pets"));
+                // check multi-valued
+                List<String> sports = (List<String>) fields.get("sports");
+                Assert.assertEquals(2, sports.size());
+                Assert.assertTrue(sports.contains("football"));
+                Assert.assertTrue(sports.contains("tennis"));
+            }
+        }
+    }
+
+    /**
+     * Data set contains a view event (id: a4aa836b-c437-48ef-be02-6fbbcba3a1de) with two interests: football:50 and basketball:30
+     * Data set contains a view event (id: 34d53399-f173-451f-8d48-f34f5d9618a9) with two URL Parameters: paramerter_test:value, multiple_paramerter_test:[value1, value2]
+     */
+    private void checkViewEventRestructured() {
+        List<Event> events = persistenceService.query("eventType", "view", null, Event.class);
+        for (Event viewEvent : events) {
+
+            // check interests
+            if (Objects.equals(viewEvent.getItemId(), "a4aa836b-c437-48ef-be02-6fbbcba3a1de")) {
+                CustomItem target = (CustomItem) viewEvent.getTarget();
+                Assert.assertNull(target.getProperties().get("interests"));
+                Map<String, Object> interests = (Map<String, Object>) viewEvent.getFlattenedProperties().get("interests");
+                Assert.assertEquals(30, interests.get("basketball"));
+                Assert.assertEquals(50, interests.get("football"));
+            }
+
+            // check URL parameters
+            if (Objects.equals(viewEvent.getItemId(), "34d53399-f173-451f-8d48-f34f5d9618a9")) {
+                CustomItem target = (CustomItem) viewEvent.getTarget();
+                Map<String, Object> pageInfo = (Map<String, Object>) target.getProperties().get("pageInfo");
+                Assert.assertNull(pageInfo.get("parameters"));
+                Map<String, Object> parameters = (Map<String, Object>) viewEvent.getFlattenedProperties().get("URLParameters");
+                Assert.assertEquals("value", parameters.get("paramerter_test"));
+                List<String> multipleParameterTest = (List<String>) parameters.get("multiple_paramerter_test");
+                Assert.assertEquals(2, multipleParameterTest.size());
+                Assert.assertTrue(multipleParameterTest.contains("value1"));
+                Assert.assertTrue(multipleParameterTest.contains("value2"));
+            }
+        }
+    }
+
+
+    /**
+     * Data set contains 2 events that are not persisted anymore:
+     * One updateProperties event
+     * One sessionCreated event
+     * This test ensures that both have been removed.
+     */
+    private void checkEventTypesNotPersistedAnymore() {
+        Assert.assertEquals(0, persistenceService.query("eventType", "updateProperties", null, Event.class).size());
+        Assert.assertEquals(0, persistenceService.query("eventType", "sessionCreated", null, Event.class).size());
+    }
+
+    /**
+     * Data set contains multiple events, this test is generic enough to ensure all existing events have the scope created correctly
+     * So the data set can contain multiple different scope it's not a problem.
+     */
+    private void checkScopeHaveBeenCreated() {
+        // check that the scope mySite have been created based on the previous existings events
+        Map<String, Long> existingScopesFromEvents = persistenceService.aggregateWithOptimizedQuery(null, new TermsAggregate("scope"), Event.ITEM_TYPE);
+        for (String scopeFromEvents : existingScopesFromEvents.keySet()) {
+            if (!Objects.equals(scopeFromEvents, "_filtered")) {
+                Scope scope = scopeService.getScope(scopeFromEvents);
+                Assert.assertNotNull(scope);
+            }
+        }
+    }
+
+    /**
+     * Data set contains a profile (id: e67ecc69-a7b3-47f1-b91f-5d6e7b90276e) with two interests: football:50 and basketball:30
+     * Also it's first name is test_profile
+     */
+    private void checkProfileInterests() {
+        // check that the test_profile interests have been migrated to new data structure
+        Profile profile = persistenceService.load("e67ecc69-a7b3-47f1-b91f-5d6e7b90276e", Profile.class);
+        Assert.assertEquals("test_profile", profile.getProperty("firstName"));
+
+        List<Map<String, Object>> interests = (List<Map<String, Object>>) profile.getProperty("interests");
+        Assert.assertEquals(2, interests.size());
+        for (Map<String, Object> interest : interests) {
+            if ("basketball".equals(interest.get("key"))) {
+                Assert.assertEquals(30, interest.get("value"));
+            }
+            if ("football".equals(interest.get("key"))) {
+                Assert.assertEquals(50, interest.get("value"));
+            }
+        }
+    }
+}
diff --git a/itests/src/test/resources/migration/create_snapshots_repository.json b/itests/src/test/resources/migration/create_snapshots_repository.json
new file mode 100644
index 000000000..5c80ddb17
--- /dev/null
+++ b/itests/src/test/resources/migration/create_snapshots_repository.json
@@ -0,0 +1,6 @@
+{
+  "type": "fs",
+  "settings": {
+    "location": "snapshots"
+  }
+}
\ No newline at end of file
diff --git a/itests/src/test/resources/migration/org.apache.unomi.migration.cfg b/itests/src/test/resources/migration/org.apache.unomi.migration.cfg
new file mode 100644
index 000000000..92214cfb1
--- /dev/null
+++ b/itests/src/test/resources/migration/org.apache.unomi.migration.cfg
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+# Migration config used for silent migration
+
+esAddress = http://localhost:9400
+httpClient.trustAllCertificates = true
+indexPrefix = context
diff --git a/itests/src/test/resources/migration/snapshots_repository.zip b/itests/src/test/resources/migration/snapshots_repository.zip
new file mode 100644
index 000000000..e0a81a755
Binary files /dev/null and b/itests/src/test/resources/migration/snapshots_repository.zip differ
diff --git a/kar/src/main/feature/feature.xml b/kar/src/main/feature/feature.xml
index b0c8db5e5..541314a8c 100644
--- a/kar/src/main/feature/feature.xml
+++ b/kar/src/main/feature/feature.xml
@@ -91,6 +91,7 @@
         <bundle start-level="85" start="false">mvn:org.apache.unomi/cxs-lists-extension-actions/${project.version}</bundle>
         <bundle start-level="85" start="false">mvn:org.apache.unomi/shell-dev-commands/${project.version}</bundle>
 
+        <configfile finalname="/etc/org.apache.unomi.migration.cfg">mvn:org.apache.unomi/shell-commands/${project.version}/cfg/migration</configfile>
         <bundle start-level="99">mvn:org.apache.unomi/shell-commands/${project.version}</bundle>
     </feature>
 
diff --git a/manual/src/main/asciidoc/shell-commands.adoc b/manual/src/main/asciidoc/shell-commands.adoc
index e951a3071..2de339d38 100644
--- a/manual/src/main/asciidoc/shell-commands.adoc
+++ b/manual/src/main/asciidoc/shell-commands.adoc
@@ -41,15 +41,21 @@ karaf@root()> help unomi:migrate
 DESCRIPTION
         unomi:migrate
 
-    This will Migrate your date in ES to be compliant with current version
+    This will Migrate your date in ES to be compliant with current version.
+    It's possible to configure the migration using OSGI configuration file: org.apache.unomi.migration.cfg,
+    if no configuration is provided then questions will be prompted during the migration process.
 
 SYNTAX
-        unomi:migrate [fromVersionWithoutSuffix]
+        unomi:migrate [fromVersionWithoutSuffix] [skipConfirmation]
 
 ARGUMENTS
         fromVersionWithoutSuffix
                 Origin version without suffix/qualifier (e.g: 1.2.0)
                 (defaults to 1.2.0)
+        skipConfirmation
+                Should the confirmation before starting the migration process be skipped ?
+                (defaults to false)
+
 ```
 ==== Lifecycle commands
 
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
index bf6efd9fa..cd5183232 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
+++ b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
@@ -459,17 +459,6 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
                 bulkProcessorFlushInterval = System.getProperty(BULK_PROCESSOR_FLUSH_INTERVAL, bulkProcessorFlushInterval);
                 bulkProcessorBackoffPolicy = System.getProperty(BULK_PROCESSOR_BACKOFF_POLICY, bulkProcessorBackoffPolicy);
                 itemsMonthlyIndexed = itemsMonthlyIndexedOverride.equals("none") ? Collections.emptyList() : Arrays.asList(System.getProperty(MONTHLY_INDEX_ITEMS_MONTHLY_INDEXED, itemsMonthlyIndexedOverride).split(",").clone());
-                // this property is used for integration tests, to make sure we don't conflict with an already running ElasticSearch instance.
-                if (System.getProperty("org.apache.unomi.itests.elasticsearch.http.port") != null) {
-                    elasticSearchAddressList.clear();
-                    elasticSearchAddressList.add("localhost:" + System.getProperty("org.apache.unomi.itests.elasticsearch.http.port"));
-                    logger.info("Overriding ElasticSearch address list from system property=" + elasticSearchAddressList);
-                }
-                // this property is used for integration tests, to make sure we don't conflict with an already running ElasticSearch instance.
-                if (System.getProperty("org.apache.unomi.itests.elasticsearch.cluster.name") != null) {
-                    clusterName = System.getProperty("org.apache.unomi.itests.elasticsearch.cluster.name");
-                    logger.info("Overriding cluster name from system property=" + clusterName);
-                }
 
                 buildClient();
 
diff --git a/pom.xml b/pom.xml
index f9d867ca9..e801b3085 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,7 @@
     <inceptionYear>2014</inceptionYear>
 
     <properties>
+        <karaf.version>4.2.15</karaf.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <cxf.version>3.3.11</cxf.version>
         <version.jackson.core>2.10.5</version.jackson.core>
diff --git a/tools/shell-commands/pom.xml b/tools/shell-commands/pom.xml
index 57ae9dc3c..e3e863d0a 100644
--- a/tools/shell-commands/pom.xml
+++ b/tools/shell-commands/pom.xml
@@ -104,6 +104,30 @@
                     </instructions>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-artifacts</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>attach-artifact</goal>
+                        </goals>
+                        <configuration>
+                            <artifacts>
+                                <artifact>
+                                    <file>
+                                        src/main/resources/org.apache.unomi.migration.cfg
+                                    </file>
+                                    <type>cfg</type>
+                                    <classifier>migration</classifier>
+                                </artifact>
+                            </artifacts>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/Migration.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/Migration.java
index 8a64882ce..c7d9ece8c 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/Migration.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/Migration.java
@@ -37,5 +37,5 @@ public interface Migration {
      * @throws IOException if there was an error while executing the migration
      * @deprecated do groovy script for implementing new migrations
      */
-    void execute(Session session, CloseableHttpClient httpClient, Map<String, Object> migrationConfig, BundleContext bundleContext) throws IOException;
+    void execute(Session session, CloseableHttpClient httpClient, MigrationConfig migrationConfig, BundleContext bundleContext) throws IOException;
 }
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/MigrationConfig.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/MigrationConfig.java
new file mode 100644
index 000000000..2e6b99587
--- /dev/null
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/MigrationConfig.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.shell.migration;
+
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.unomi.shell.migration.utils.ConsoleUtils;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Service uses to aggregate different configuration needed by the migrations
+ * Source of config:
+ * - file system in OSGI config file: org.apache.unomi.migration.cfg
+ * - user interactions in the console during the migration process
+ */
+@Component(immediate = true, service = MigrationConfig.class, configurationPid = {"org.apache.unomi.migration"})
+public class MigrationConfig {
+
+    public static final String CONFIG_ES_ADDRESS = "esAddress";
+    public static final String CONFIG_TRUST_ALL_CERTIFICATES = "httpClient.trustAllCertificates";
+    public static final String INDEX_PREFIX = "indexPrefix";
+    public static final String NUMBER_OF_SHARDS = "number_of_shards";
+    public static final String NUMBER_OF_REPLICAS = "number_of_replicas";
+    public static final String TOTAL_FIELDS_LIMIT = "mapping.total_fields.limit";
+    public static final String MAX_DOC_VALUE_FIELDS_SEARCH = "max_docvalue_fields_search";
+
+    private static final Map<String, MigrationConfigProperty> configProperties;
+    static {
+        Map<String, MigrationConfigProperty> m = new HashMap<>();
+        m.put(CONFIG_ES_ADDRESS, new MigrationConfigProperty("Enter ElasticSearch TARGET address (default: http://localhost:9200): ", "http://localhost:9200"));
+        m.put(CONFIG_TRUST_ALL_CERTIFICATES, new MigrationConfigProperty("We need to initialize a HttpClient, do we need to trust all certificates ?", null));
+        m.put(INDEX_PREFIX, new MigrationConfigProperty("Enter ElasticSearch Unomi indices prefix (default: context): ", "context"));
+        m.put(NUMBER_OF_SHARDS, new MigrationConfigProperty("Enter ElasticSearch index mapping configuration: number_of_shards (default: 3): ", "3"));
+        m.put(NUMBER_OF_REPLICAS, new MigrationConfigProperty("Enter ElasticSearch index mapping configuration: number_of_replicas (default: 0): ", "0"));
+        m.put(TOTAL_FIELDS_LIMIT, new MigrationConfigProperty("Enter ElasticSearch index mapping configuration: mapping.total_fields.limit (default: 1000): ", "1000"));
+        m.put(MAX_DOC_VALUE_FIELDS_SEARCH, new MigrationConfigProperty("Enter ElasticSearch index mapping configuration: max_docvalue_fields_search (default: 1000): ", "1000"));
+        configProperties = Collections.unmodifiableMap(m);
+    }
+
+    Map<String, String> initialConfig = new HashMap<>();
+    Map<String, String> computeConfig = new HashMap<>();
+
+    @Activate
+    @Modified
+    public void modified(Map<String, String> config) {
+        initialConfig = config;
+        reset();
+    }
+
+    /**
+     * Used reset user choices to initial file system config (useful at the beginning of each new migrate session)
+     */
+    public void reset() {
+        computeConfig.clear();
+        computeConfig.putAll(initialConfig);
+    }
+
+    public String getString(String name, Session session) throws IOException {
+        if (computeConfig.containsKey(name)) {
+            return computeConfig.get(name);
+        }
+        if (configProperties.containsKey(name)) {
+            MigrationConfigProperty migrateConfigProperty = configProperties.get(name);
+            String answer = ConsoleUtils.askUserWithDefaultAnswer(session, migrateConfigProperty.getDescription(), migrateConfigProperty.getDefaultValue());
+            computeConfig.put(name, answer);
+            return answer;
+        }
+        return null;
+    }
+
+    public boolean getBoolean(String name, Session session) throws IOException {
+        if (computeConfig.containsKey(name)) {
+            return Boolean.parseBoolean(computeConfig.get(name));
+        }
+        if (configProperties.containsKey(name)) {
+            MigrationConfigProperty migrateConfigProperty = configProperties.get(name);
+            boolean answer = ConsoleUtils.askUserWithAuthorizedAnswer(session, migrateConfigProperty.getDescription(), Arrays.asList("yes", "no")).equalsIgnoreCase("yes");
+            computeConfig.put(name, answer ? "true" : "false");
+            return answer;
+        }
+        return false;
+    }
+}
diff --git a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-02-scoringPlanReindex.groovy b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/MigrationConfigProperty.java
similarity index 60%
rename from tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-02-scoringPlanReindex.groovy
rename to tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/MigrationConfigProperty.java
index 46e898117..58f4bee75 100644
--- a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-02-scoringPlanReindex.groovy
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/MigrationConfigProperty.java
@@ -1,5 +1,3 @@
-import org.apache.unomi.shell.migration.utils.MigrationUtils
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -16,7 +14,25 @@ import org.apache.unomi.shell.migration.utils.MigrationUtils
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.unomi.shell.migration;
+
+/**
+ * Just a bean for a configuration property to be used during migration process
+ */
+public class MigrationConfigProperty {
+    String description;
+    String defaultValue;
+
+    public MigrationConfigProperty(String description, String defaultValue) {
+        this.description = description;
+        this.defaultValue = defaultValue;
+    }
+
+    public String getDescription() {
+        return description;
+    }
 
-String newIndexSettings = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/scoring_index.json");
-MigrationUtils.reIndex(httpClient, bundleContext, migrationConfig.get("esAddress"), migrationConfig.get("indexPrefix") + "-scoring",
-        newIndexSettings, null)
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+}
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/actions/Migrate.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/actions/Migrate.java
index 80778ec2b..dd4946d5c 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/actions/Migrate.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/actions/Migrate.java
@@ -26,6 +26,7 @@ import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.apache.karaf.shell.api.console.Session;
+import org.apache.unomi.shell.migration.MigrationConfig;
 import org.apache.unomi.shell.migration.MigrationScript;
 import org.apache.unomi.shell.migration.utils.ConsoleUtils;
 import org.apache.unomi.shell.migration.utils.HttpUtils;
@@ -41,12 +42,13 @@ import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-@Command(scope = "unomi", name = "migrate", description = "This will Migrate your data in ES to be compliant with current version")
+import static org.apache.unomi.shell.migration.MigrationConfig.CONFIG_TRUST_ALL_CERTIFICATES;
+
+@Command(scope = "unomi", name = "migrate", description = "This will Migrate your data in ES to be compliant with current version. " +
+        "It's possible to configure the migration using OSGI configuration file: org.apache.unomi.migration.cfg, if no configuration is provided then questions will be prompted during the migration process.")
 @Service
 public class Migrate implements Action {
-    public static final String CONFIG_ES_ADDRESS = "esAddress";
-    public static final String CONFIG_TRUST_ALL_CERTIFICATES = "httpClient.trustAllCertificates";
-    public static final String INDEX_PREFIX = "indexPrefix";
+
 
     @Reference
     Session session;
@@ -54,9 +56,15 @@ public class Migrate implements Action {
     @Reference
     BundleContext bundleContext;
 
+    @Reference
+    MigrationConfig migrationConfig;
+
     @Argument(name = "originVersion", description = "Origin version without suffix/qualifier (e.g: 1.2.0)", valueToShowInHelp = "1.2.0")
     private String originVersion;
 
+    @Argument(index = 1, name = "skipConfirmation", description = "Should the confirmation before starting the migration process be skipped ?", valueToShowInHelp = "false")
+    private boolean skipConfirmation = false;
+
     public Object execute() throws Exception {
         // Load migration scrips
         Set<MigrationScript> scripts = loadOSGIScripts();
@@ -80,20 +88,17 @@ public class Migrate implements Action {
         }
 
         // Check for user approval before migrate
-        if (ConsoleUtils.askUserWithAuthorizedAnswer(session,
+        if (!skipConfirmation && ConsoleUtils.askUserWithAuthorizedAnswer(session,
                 "[WARNING] You are about to execute a migration, this a very sensitive operation, are you sure? (yes/no): ",
                 Arrays.asList("yes", "no")).equalsIgnoreCase("no")) {
             ConsoleUtils.printMessage(session, "Migration process aborted");
             return null;
         }
 
-        // Build conf
-        Map<String, Object> migrationConfig = new HashMap<>();
-        migrationConfig.put(CONFIG_ES_ADDRESS, ConsoleUtils.askUserWithDefaultAnswer(session, "Enter ElasticSearch 7 TARGET address (default = http://localhost:9200): ", "http://localhost:9200"));
-        migrationConfig.put(CONFIG_TRUST_ALL_CERTIFICATES, ConsoleUtils.askUserWithAuthorizedAnswer(session,"We need to initialize a HttpClient, do we need to trust all certificates? (yes/no): ", Arrays.asList("yes", "no")).equalsIgnoreCase("yes"));
-        migrationConfig.put(INDEX_PREFIX, ConsoleUtils.askUserWithDefaultAnswer(session, "SOURCE index name (default: context) : ", "context"));
-
-        try (CloseableHttpClient httpClient = HttpUtils.initHttpClient((Boolean) migrationConfig.get(CONFIG_TRUST_ALL_CERTIFICATES))) {
+        // reset migration config from previous stored users choices.
+        migrationConfig.reset();
+        
+        try (CloseableHttpClient httpClient = HttpUtils.initHttpClient(migrationConfig.getBoolean(CONFIG_TRUST_ALL_CERTIFICATES, session))) {
 
             // Compile scripts
             scripts = parseScripts(scripts, session, httpClient, migrationConfig);
@@ -133,7 +138,7 @@ public class Migrate implements Action {
                 .collect(Collectors.toCollection(TreeSet::new));
     }
 
-    private Set<MigrationScript> parseScripts(Set<MigrationScript> scripts, Session session, CloseableHttpClient httpClient, Map<String, Object> migrationConfig) {
+    private Set<MigrationScript> parseScripts(Set<MigrationScript> scripts, Session session, CloseableHttpClient httpClient, MigrationConfig migrationConfig) {
         Map<String, GroovyShell> shellsPerBundle = new HashMap<>();
 
         return scripts.stream()
@@ -187,7 +192,7 @@ public class Migrate implements Action {
         return migrationScripts;
     }
 
-    private GroovyShell buildShellForBundle(Bundle bundle, Session session, CloseableHttpClient httpClient, Map<String, Object> migrationConfig) {
+    private GroovyShell buildShellForBundle(Bundle bundle, Session session, CloseableHttpClient httpClient, MigrationConfig migrationConfig) {
         GroovyClassLoader groovyLoader = new GroovyClassLoader(bundle.adapt(BundleWiring.class).getClassLoader());
         GroovyScriptEngine groovyScriptEngine = new GroovyScriptEngine((URL[]) null, groovyLoader);
         GroovyShell groovyShell = new GroovyShell(groovyScriptEngine.getGroovyClassLoader());
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo121.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo121.java
index 06183296f..072e97035 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo121.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo121.java
@@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.karaf.shell.api.console.Session;
 import org.apache.unomi.shell.migration.Migration;
+import org.apache.unomi.shell.migration.MigrationConfig;
 import org.apache.unomi.shell.migration.utils.ConsoleUtils;
 import org.apache.unomi.shell.migration.utils.MigrationUtils;
 import org.json.JSONArray;
@@ -41,10 +42,10 @@ public class MigrationTo121 implements Migration {
     private List propsTaggedAsPersonalIdentifier = Arrays.asList("firstName", "lastName", "email", "phoneNumber", "address", "facebookId", "googleId", "linkedInId", "twitterId");
 
     @Override
-    public void execute(Session session, CloseableHttpClient httpClient, Map<String, Object> migrationConfig, BundleContext bundleContext) throws IOException {
+    public void execute(Session session, CloseableHttpClient httpClient, MigrationConfig migrationConfig, BundleContext bundleContext) throws IOException {
         this.httpClient = httpClient;
         this.session = session;
-        this.esAddress = (String) migrationConfig.get("esAddress");
+        this.esAddress = migrationConfig.getString(MigrationConfig.CONFIG_ES_ADDRESS, session);
         migrateTags();
     }
 
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo122.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo122.java
index b371f8a0d..5ad9c28ac 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo122.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo122.java
@@ -19,6 +19,7 @@ package org.apache.unomi.shell.migration.impl;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.karaf.shell.api.console.Session;
 import org.apache.unomi.shell.migration.Migration;
+import org.apache.unomi.shell.migration.MigrationConfig;
 import org.apache.unomi.shell.migration.utils.ConsoleUtils;
 import org.apache.unomi.shell.migration.utils.HttpRequestException;
 import org.apache.unomi.shell.migration.utils.HttpUtils;
@@ -34,10 +35,10 @@ public class MigrationTo122 implements Migration {
     private String esAddress;
 
     @Override
-    public void execute(Session session, CloseableHttpClient httpClient, Map<String, Object> migrationConfig, BundleContext bundleContext) throws IOException {
+    public void execute(Session session, CloseableHttpClient httpClient, MigrationConfig migrationConfig, BundleContext bundleContext) throws IOException {
         this.httpClient = httpClient;
         this.session = session;
-        this.esAddress = (String) migrationConfig.get("esAddress");
+        this.esAddress = migrationConfig.getString(MigrationConfig.CONFIG_ES_ADDRESS, session);
         deleteOldIndexTemplate();
 
     }
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo150.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo150.java
index 4eb84c821..074a7fabf 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo150.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo150.java
@@ -19,6 +19,7 @@ package org.apache.unomi.shell.migration.impl;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.karaf.shell.api.console.Session;
 import org.apache.unomi.shell.migration.Migration;
+import org.apache.unomi.shell.migration.MigrationConfig;
 import org.apache.unomi.shell.migration.utils.ConsoleUtils;
 import org.apache.unomi.shell.migration.utils.HttpUtils;
 import org.json.JSONArray;
@@ -37,10 +38,10 @@ public class MigrationTo150 implements Migration {
     public static final String INDEX_DATE_PREFIX = "date-";
 
     @Override
-    public void execute(Session session, CloseableHttpClient httpClient, Map<String, Object> migrationConfig, BundleContext bundleContext) throws IOException {
-        String esAddress = (String) migrationConfig.get("esAddress");
+    public void execute(Session session, CloseableHttpClient httpClient, MigrationConfig migrationConfig, BundleContext bundleContext) throws IOException {
+        String esAddress = migrationConfig.getString(MigrationConfig.CONFIG_ES_ADDRESS, session);
         String es5Address = ConsoleUtils.askUserWithDefaultAnswer(session, "SOURCE Elasticsearch 5.6 cluster address (default: http://localhost:9210) : ", "http://localhost:9210");
-        String sourceIndexPrefix = (String) migrationConfig.get("indexPrefix");
+        String sourceIndexPrefix = migrationConfig.getString(MigrationConfig.INDEX_PREFIX, session);
         String destIndexPrefix = ConsoleUtils.askUserWithDefaultAnswer(session, "TARGET index prefix (default: context) : ", "context");
         int numberOfShards = Integer.parseInt(ConsoleUtils.askUserWithDefaultAnswer(session, "Number of shards for TARGET (default: 5) : ", "5"));
         int numberOfReplicas = Integer.parseInt(ConsoleUtils.askUserWithDefaultAnswer(session, "Number of replicas for TARGET (default: 1) : ", "1"));
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo200.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo200.java
index 3e8967ed0..b1de1815d 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo200.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/impl/MigrationTo200.java
@@ -27,6 +27,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.util.EntityUtils;
 import org.apache.karaf.shell.api.console.Session;
 import org.apache.unomi.shell.migration.Migration;
+import org.apache.unomi.shell.migration.MigrationConfig;
 import org.apache.unomi.shell.migration.utils.ConsoleUtils;
 import org.apache.unomi.shell.migration.utils.MigrationUtils;
 import org.json.JSONArray;
@@ -48,26 +49,30 @@ public class MigrationTo200 implements Migration {
     private CloseableHttpClient httpClient;
     private Session session;
     private String esAddress;
+    private String indexPrefix;
     private BundleContext bundleContext;
+    private MigrationConfig migrationConfig;
 
     @Override
-    public void execute(Session session, CloseableHttpClient httpClient, Map<String, Object> migrationConfig, BundleContext bundleContext) throws IOException {
+    public void execute(Session session, CloseableHttpClient httpClient, MigrationConfig migrationConfig, BundleContext bundleContext) throws IOException {
         this.httpClient = httpClient;
         this.session = session;
-        this.esAddress = (String) migrationConfig.get("esAddress");
+        this.esAddress = migrationConfig.getString(MigrationConfig.CONFIG_ES_ADDRESS, session);
+        this.indexPrefix = migrationConfig.getString(MigrationConfig.INDEX_PREFIX, session);
         this.bundleContext = bundleContext;
+        this.migrationConfig = migrationConfig;
 
-        doExecute((String) migrationConfig.get("indexPrefix"));
+        doExecute();
     }
 
-    private void doExecute(String indexPrefix) throws IOException {
+    private void doExecute() throws IOException {
         Set<String> indexes = MigrationUtils.getIndexesPrefixedBy(httpClient, esAddress, indexPrefix + "-event-");
-        createScopeMapping(indexPrefix);
-        createScopes(getSetOfScopes(indexes), indexPrefix);
+        createScopeMapping();
+        createScopes(getSetOfScopes(indexes));
         createProfileAliasDocumentsFromProfile();
     }
 
-    private boolean scopeIndexNotExists(String indexPrefix) throws IOException {
+    private boolean scopeIndexNotExists() throws IOException {
         final HttpGet httpGet = new HttpGet(esAddress + "/" + indexPrefix + "-scope");
 
         httpGet.addHeader("Accept", "application/json");
@@ -78,26 +83,19 @@ public class MigrationTo200 implements Migration {
         }
     }
 
-    private void createScopeMapping(String indexPrefix) throws IOException {
+    private void createScopeMapping() throws IOException {
 
-        if (scopeIndexNotExists(indexPrefix)) {
+        if (scopeIndexNotExists()) {
             System.out.println("Creation for index = \"" + indexPrefix + "-scope\" starting.");
-            System.out.println("Specify the following parameters:");
-            String numberOfShards = ConsoleUtils.askUserWithDefaultAnswer(session, "number_of_shards: (default: 3)", "3");
-            String numberOfReplicas = ConsoleUtils.askUserWithDefaultAnswer(session, "number_of_replicas: (default: 0)", "0");
-            String mappingTotalFieldsLimit = ConsoleUtils
-                    .askUserWithDefaultAnswer(session, "mapping.total_fields.limit: (default: 1000)", "1000");
-            String maxDocValueFieldsSearch = ConsoleUtils
-                    .askUserWithDefaultAnswer(session, "max_docvalue_fields_search: (default: 1000)", "1000");
-
             final HttpPut httpPost = new HttpPut(esAddress + "/" + indexPrefix + "-scope");
 
             httpPost.addHeader("Accept", "application/json");
             httpPost.addHeader("Content-Type", "application/json");
 
-            String request = MigrationUtils.resourceAsString(bundleContext,"requestBody/scopeMapping.json").replace("$numberOfShards", numberOfShards)
-                    .replace("$numberOfReplicas", numberOfReplicas).replace("$mappingTotalFieldsLimit", mappingTotalFieldsLimit)
-                    .replace("$maxDocValueFieldsSearch", maxDocValueFieldsSearch);
+            String baseRequest = MigrationUtils.resourceAsString(bundleContext,"requestBody/2.0.0/base_index_mapping.json");
+            String mapping = MigrationUtils.extractMappingFromBundles(bundleContext, "scope.json");
+            // We intentionally extract setting from profile index, because the scope index doesnt exist yet, and all indices share the same configuration regarding shards, replicas, etc ..
+            String request = MigrationUtils.buildIndexCreationRequest(httpClient, esAddress, baseRequest, indexPrefix + "-profile", mapping);
 
             httpPost.setEntity(new StringEntity(request));
 
@@ -116,7 +114,7 @@ public class MigrationTo200 implements Migration {
 
     }
 
-    private void createScopes(Set<String> scopes, String indexPrefix) throws IOException {
+    private void createScopes(Set<String> scopes) throws IOException {
         final StringBuilder body = new StringBuilder();
         String saveScopeBody = MigrationUtils.resourceAsString(bundleContext,"requestBody/bulkSaveScope.ndjson");
         scopes.forEach(scope -> body.append(saveScopeBody.replace("$scope", scope)));
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java
index 33b0278ed..e93998b50 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java
@@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.util.EntityUtils;
 import org.json.JSONObject;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
 import java.io.BufferedReader;
@@ -34,6 +35,7 @@ import java.io.InputStreamReader;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -97,17 +99,43 @@ public class MigrationUtils {
         return Collections.emptySet();
     }
 
+    public static String extractMappingFromBundles(BundleContext bundleContext, String fileName) throws IOException {
+        for (Bundle bundle : bundleContext.getBundles()) {
+            Enumeration<URL> predefinedMappings = bundle.findEntries("META-INF/cxs/mappings", fileName, true);
+            if (predefinedMappings == null) {
+                continue;
+            }
+            while (predefinedMappings.hasMoreElements()) {
+                URL predefinedMappingURL = predefinedMappings.nextElement();
+                return IOUtils.toString(predefinedMappingURL);
+            }
+        }
+
+        throw new RuntimeException("no mapping found in bundles for: " + fileName);
+    }
+
+    public static String buildIndexCreationRequest(CloseableHttpClient httpClient, String esAddress, String baseIndexSettings,
+                                            String originalIndexForSettingsExtraction, String mapping) throws IOException {
+
+        String settings = baseIndexSettings;
+
+        // Extract existing settings on index that still exists
+        if (originalIndexForSettingsExtraction != null) {
+            JSONObject originalIndexSettings = new JSONObject(HttpUtils.executeGetRequest(httpClient, esAddress + "/" + originalIndexForSettingsExtraction + "/_settings", null));
+            settings = settings
+                    .replace("#numberOfShards", originalIndexSettings.getJSONObject(originalIndexForSettingsExtraction).getJSONObject("settings").getJSONObject("index").getString("number_of_shards"))
+                    .replace("#numberOfReplicas", originalIndexSettings.getJSONObject(originalIndexForSettingsExtraction).getJSONObject("settings").getJSONObject("index").getString("number_of_replicas"))
+                    .replace("#maxDocValueFieldsSearch", originalIndexSettings.getJSONObject(originalIndexForSettingsExtraction).getJSONObject("settings").getJSONObject("index").getString("max_docvalue_fields_search"))
+                    .replace("#mappingTotalFieldsLimit", originalIndexSettings.getJSONObject(originalIndexForSettingsExtraction).getJSONObject("settings").getJSONObject("index").getJSONObject("mapping").getJSONObject("total_fields").getString("limit"));
+        }
+
+        return settings.replace("#mappings", mapping);
+    }
+
     public static void reIndex(CloseableHttpClient httpClient, BundleContext bundleContext, String esAddress, String indexName,
             String newIndexSettings, String painlessScript) throws IOException {
         String indexNameCloned = indexName + "-cloned";
 
-        // Init requests
-        JSONObject originalIndexSettings = new JSONObject(HttpUtils.executeGetRequest(httpClient, esAddress + "/" + indexName + "/_settings", null));
-        String newIndexRequest = newIndexSettings
-                .replace("#numberOfShards", originalIndexSettings.getJSONObject(indexName).getJSONObject("settings").getJSONObject("index").getString("number_of_shards"))
-                .replace("#numberOfReplicas", originalIndexSettings.getJSONObject(indexName).getJSONObject("settings").getJSONObject("index").getString("number_of_replicas"))
-                .replace("#maxDocValueFieldsSearch", originalIndexSettings.getJSONObject(indexName).getJSONObject("settings").getJSONObject("index").getString("max_docvalue_fields_search"))
-                .replace("#mappingTotalFieldsLimit", originalIndexSettings.getJSONObject(indexName).getJSONObject("settings").getJSONObject("index").getJSONObject("mapping").getJSONObject("total_fields").getString("limit"));
         String reIndexRequest = resourceAsString(bundleContext, "requestBody/2.0.0/base_reindex_request.json")
                 .replace("#source", indexNameCloned).replace("#dest", indexName)
                 .replace("#painless", StringUtils.isNotEmpty(painlessScript) ? getScriptPart(painlessScript) : "");
@@ -121,7 +149,7 @@ public class MigrationUtils {
         // Delete original index
         HttpUtils.executeDeleteRequest(httpClient, esAddress + "/" + indexName, null);
         // Recreate the original index with new mappings
-        HttpUtils.executePutRequest(httpClient, esAddress + "/" + indexName, newIndexRequest, null);
+        HttpUtils.executePutRequest(httpClient, esAddress + "/" + indexName, newIndexSettings, null);
         // Reindex data from clone
         HttpUtils.executePostRequest(httpClient, esAddress + "/_reindex", reIndexRequest, null);
         // Remove clone
diff --git a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-01-segmentReindex.groovy b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-01-globalReindex.groovy
similarity index 50%
rename from tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-01-segmentReindex.groovy
rename to tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-01-globalReindex.groovy
index 96048e37f..24603d75e 100644
--- a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-01-segmentReindex.groovy
+++ b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-01-globalReindex.groovy
@@ -17,6 +17,15 @@ import org.apache.unomi.shell.migration.utils.MigrationUtils
  * limitations under the License.
  */
 
-String newIndexSettings = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/segment_index.json");
-MigrationUtils.reIndex(httpClient, bundleContext, migrationConfig.get("esAddress"), migrationConfig.get("indexPrefix") + "-segment",
-        newIndexSettings, null)
+String esAddress = migrationConfig.getString("esAddress", session)
+String indexPrefix = migrationConfig.getString("indexPrefix", session)
+
+String baseSettings = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/base_index_mapping.json")
+String[] indicesToReindex = ["segment", "scoring", "campaign", "conditionType", "goal", "patch", "rule"];
+indicesToReindex.each { indexToReindex ->
+    String mappingFileName = "${indexToReindex}.json"
+    String realIndexName = "${indexPrefix}-${indexToReindex.toLowerCase()}"
+    String mapping = MigrationUtils.extractMappingFromBundles(bundleContext, mappingFileName)
+    String newIndexSettings = MigrationUtils.buildIndexCreationRequest(httpClient, esAddress, baseSettings, realIndexName, mapping)
+    MigrationUtils.reIndex(httpClient, bundleContext, esAddress, realIndexName, newIndexSettings, null)
+}
diff --git a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-03-profileReindex.groovy b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-03-profileReindex.groovy
index 83ab51233..1fae7ffdb 100644
--- a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-03-profileReindex.groovy
+++ b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-03-profileReindex.groovy
@@ -17,6 +17,11 @@ import org.apache.unomi.shell.migration.utils.MigrationUtils
  * limitations under the License.
  */
 
-String newIndexSettings = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/profile_index.json");
-MigrationUtils.reIndex(httpClient, bundleContext, migrationConfig.get("esAddress"), migrationConfig.get("indexPrefix") + "-profile",
+String esAddress = migrationConfig.getString("esAddress", session)
+String indexPrefix = migrationConfig.getString("indexPrefix", session)
+
+String baseSettings = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/base_index_mapping.json")
+String mapping = MigrationUtils.extractMappingFromBundles(bundleContext, "profile.json")
+String newIndexSettings = MigrationUtils.buildIndexCreationRequest(httpClient, esAddress, baseSettings, indexPrefix + "-profile", mapping)
+MigrationUtils.reIndex(httpClient, bundleContext, esAddress, indexPrefix + "-profile",
         newIndexSettings, MigrationUtils.getFileWithoutComments(bundleContext, "requestBody/2.0.0/profile_migrate.painless"))
diff --git a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-04-eventsReindex.groovy b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-04-eventsReindex.groovy
index 10c64646f..aaad0599b 100644
--- a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-04-eventsReindex.groovy
+++ b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.0.0-04-eventsReindex.groovy
@@ -18,17 +18,19 @@ import org.apache.unomi.shell.migration.utils.MigrationUtils
  * limitations under the License.
  */
 
-String esAddress = migrationConfig.get("esAddress")
-String indexPrefix = migrationConfig.get("indexPrefix")
+String esAddress = migrationConfig.getString("esAddress", session)
+String indexPrefix = migrationConfig.getString("indexPrefix", session)
 
 // Remove all internal events that are no more persisted
 String removeInternalEventsRequest = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/event_delete_by_query.json")
 HttpUtils.executePostRequest(httpClient, "${esAddress}/${indexPrefix}-event-*/_delete_by_query", removeInternalEventsRequest, null)
 
 // Reindex the rest of the events
-String newIndexSettings = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/event_index.json");
+String baseSettings = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.0.0/base_index_mapping.json")
 String reIndexScript = MigrationUtils.getFileWithoutComments(bundleContext, "requestBody/2.0.0/event_migrate.painless");
+String mapping = MigrationUtils.extractMappingFromBundles(bundleContext, "event.json")
 Set<String> eventIndices = MigrationUtils.getIndexesPrefixedBy(httpClient, esAddress, "${indexPrefix}-event-")
 eventIndices.each { eventIndex ->
+    String newIndexSettings = MigrationUtils.buildIndexCreationRequest(httpClient, esAddress, baseSettings, eventIndex, mapping)
     MigrationUtils.reIndex(httpClient, bundleContext, esAddress, eventIndex, newIndexSettings, reIndexScript)
 }
\ No newline at end of file
diff --git a/tools/shell-commands/src/main/resources/org.apache.unomi.migration.cfg b/tools/shell-commands/src/main/resources/org.apache.unomi.migration.cfg
new file mode 100644
index 000000000..960020cee
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/org.apache.unomi.migration.cfg
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+# Migration config used for silent migration
+
+# esAddress = http://localhost:9200
+# httpClient.trustAllCertificates = true
+# indexPrefix = context
\ No newline at end of file
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.0.0/base_index_mapping.json b/tools/shell-commands/src/main/resources/requestBody/2.0.0/base_index_mapping.json
new file mode 100644
index 000000000..a5de5759a
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/requestBody/2.0.0/base_index_mapping.json
@@ -0,0 +1,23 @@
+{
+  "settings": {
+    "index": {
+      "number_of_shards": #numberOfShards,
+      "number_of_replicas": #numberOfReplicas,
+      "mapping.total_fields.limit": #mappingTotalFieldsLimit,
+      "max_docvalue_fields_search": #maxDocValueFieldsSearch
+    },
+    "analysis": {
+      "analyzer": {
+        "folding": {
+          "type": "custom",
+          "tokenizer": "keyword",
+          "filter": [
+            "lowercase",
+            "asciifolding"
+          ]
+        }
+      }
+    }
+  },
+  "mappings": #mappings
+}
\ No newline at end of file
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.0.0/event_index.json b/tools/shell-commands/src/main/resources/requestBody/2.0.0/event_index.json
deleted file mode 100644
index a0e25f945..000000000
--- a/tools/shell-commands/src/main/resources/requestBody/2.0.0/event_index.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
-  "settings": {
-    "index": {
-      "number_of_shards": #numberOfShards,
-      "number_of_replicas": #numberOfReplicas,
-      "mapping.total_fields.limit": #mappingTotalFieldsLimit,
-      "max_docvalue_fields_search": #maxDocValueFieldsSearch
-    },
-    "analysis": {
-      "analyzer": {
-        "folding": {
-          "type": "custom",
-          "tokenizer": "keyword",
-          "filter": [
-            "lowercase",
-            "asciifolding"
-          ]
-        }
-      }
-    }
-  },
-  "mappings": {
-    "dynamic_templates": [
-      {
-        "all": {
-          "match": "*",
-          "match_mapping_type": "string",
-          "mapping": {
-            "type": "text",
-            "analyzer": "folding",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          }
-        }
-      }
-    ],
-    "properties": {
-      "flattenedProperties": {
-        "type": "flattened"
-      },
-      "timeStamp": {
-        "type": "date"
-      },
-      "target" : {
-        "properties" : {
-          "lastEventDate" : {
-            "type" : "date"
-          },
-          "profile" : {
-            "properties" : {
-              "properties" : {
-                "properties" : {
-                  "birthDate" : {
-                    "type" : "date"
-                  },
-                  "firstVisit" : {
-                    "type" : "date"
-                  },
-                  "lastVisit" : {
-                    "type" : "date"
-                  },
-                  "notificationRefreshDate" : {
-                    "type" : "date"
-                  },
-                  "previousVisit" : {
-                    "type" : "date"
-                  }
-                }
-              },
-              "systemProperties" : {
-                "properties" : {
-
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-
-}
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.0.0/profile_index.json b/tools/shell-commands/src/main/resources/requestBody/2.0.0/profile_index.json
deleted file mode 100644
index 0808aa03e..000000000
--- a/tools/shell-commands/src/main/resources/requestBody/2.0.0/profile_index.json
+++ /dev/null
@@ -1,76 +0,0 @@
-{
-    "settings": {
-        "index": {
-            "number_of_shards": #numberOfShards,
-            "number_of_replicas": #numberOfReplicas,
-            "mapping.total_fields.limit": #mappingTotalFieldsLimit,
-            "max_docvalue_fields_search": #maxDocValueFieldsSearch
-        },
-        "analysis": {
-            "analyzer": {
-                "folding": {
-                    "type": "custom",
-                    "tokenizer": "keyword",
-                    "filter": [
-                        "lowercase",
-                        "asciifolding"
-                    ]
-                }
-            }
-        }
-    },
-    "mappings": {
-        "dynamic_templates": [
-            {
-                "all": {
-                    "match": "*",
-                    "match_mapping_type": "string",
-                    "mapping": {
-                        "type": "text",
-                        "analyzer": "folding",
-                        "fields": {
-                            "keyword": {
-                                "type": "keyword",
-                                "ignore_above": 256
-                            }
-                        }
-                    }
-                }
-            }
-        ],
-        "properties": {
-            "properties": {
-                "properties": {
-                    "age": {
-                        "type": "long"
-                    },
-                    "firstVisit": {
-                        "type": "date"
-                    },
-                    "lastVisit": {
-                        "type": "date"
-                    },
-                    "previousVisit": {
-                        "type": "date"
-                    },
-                    "nbOfVisits": {
-                        "type": "long"
-                    },
-                    "interests": {
-                        "type": "nested"
-                    }
-                }
-            },
-            "consents": {
-                "properties": {
-                    "statusDate": {
-                        "type": "date"
-                    },
-                    "revokeDate": {
-                        "type": "date"
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.0.0/scoring_index.json b/tools/shell-commands/src/main/resources/requestBody/2.0.0/scoring_index.json
deleted file mode 100644
index d2541861d..000000000
--- a/tools/shell-commands/src/main/resources/requestBody/2.0.0/scoring_index.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
-  "settings": {
-    "index": {
-      "number_of_shards": #numberOfShards,
-      "number_of_replicas": #numberOfReplicas,
-      "mapping.total_fields.limit": #mappingTotalFieldsLimit,
-      "max_docvalue_fields_search": #maxDocValueFieldsSearch
-    },
-    "analysis": {
-      "analyzer": {
-        "folding": {
-          "type": "custom",
-          "tokenizer": "keyword",
-          "filter": [
-            "lowercase",
-            "asciifolding"
-          ]
-        }
-      }
-    }
-  },
-  "mappings": {
-    "dynamic_templates": [
-      {
-        "all": {
-          "match": "*",
-          "match_mapping_type": "string",
-          "mapping": {
-            "type": "text",
-            "analyzer": "folding",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          }
-        }
-      }
-    ],
-    "properties": {
-      "metadata": {
-        "properties": {
-          "enabled": {
-            "type": "boolean"
-          },
-          "hidden": {
-            "type": "boolean"
-          },
-          "missingPlugins": {
-            "type": "boolean"
-          },
-          "readOnly": {
-            "type": "boolean"
-          }
-        }
-      },
-      "elements": {
-        "properties": {
-          "condition": {
-            "type": "object",
-            "enabled": false
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.0.0/segment_index.json b/tools/shell-commands/src/main/resources/requestBody/2.0.0/segment_index.json
deleted file mode 100644
index ec8a3c336..000000000
--- a/tools/shell-commands/src/main/resources/requestBody/2.0.0/segment_index.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-  "settings": {
-    "index": {
-      "number_of_shards": #numberOfShards,
-      "number_of_replicas": #numberOfReplicas,
-      "mapping.total_fields.limit": #mappingTotalFieldsLimit,
-      "max_docvalue_fields_search": #maxDocValueFieldsSearch
-    },
-    "analysis": {
-      "analyzer": {
-        "folding": {
-          "type": "custom",
-          "tokenizer": "keyword",
-          "filter": [
-            "lowercase",
-            "asciifolding"
-          ]
-        }
-      }
-    }
-  },
-  "mappings": {
-    "dynamic_templates": [
-      {
-        "all": {
-          "match": "*",
-          "match_mapping_type": "string",
-          "mapping": {
-            "type": "text",
-            "analyzer": "folding",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          }
-        }
-      }
-    ],
-    "properties": {
-      "metadata": {
-        "properties": {
-          "enabled": {
-            "type": "boolean"
-          },
-          "hidden": {
-            "type": "boolean"
-          },
-          "missingPlugins": {
-            "type": "boolean"
-          },
-          "readOnly": {
-            "type": "boolean"
-          }
-        }
-      },
-      "condition": {
-        "type": "object",
-        "enabled": false
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tools/shell-commands/src/main/resources/requestBody/scopeMapping.json b/tools/shell-commands/src/main/resources/requestBody/scopeMapping.json
deleted file mode 100644
index b45b0a851..000000000
--- a/tools/shell-commands/src/main/resources/requestBody/scopeMapping.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
-  "settings": {
-    "index": {
-      "number_of_shards": $numberOfShards,
-      "number_of_replicas": $numberOfReplicas,
-      "mapping.total_fields.limit": $mappingTotalFieldsLimit,
-      "max_docvalue_fields_search": $maxDocValueFieldsSearch
-    },
-    "analysis": {
-      "analyzer": {
-        "folding": {
-          "type": "custom",
-          "tokenizer": "keyword",
-          "filter": [
-            "lowercase",
-            "asciifolding"
-          ]
-        }
-      }
-    }
-  },
-  "mappings": {
-    "dynamic_templates": [
-      {
-        "all": {
-          "match": "*",
-          "match_mapping_type": "string",
-          "mapping": {
-            "type": "text",
-            "analyzer": "folding",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          }
-        }
-      }
-    ],
-    "properties": {
-    }
-  }
-}