You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by dg...@apache.org on 2017/10/06 13:33:27 UTC

[03/15] incubator-unomi git commit: UNOMI-117 Create tools package that contain a shell bundle so we can define our custom shell commands for unomi, create a migration shell command to ease the migration to a new version

UNOMI-117 Create tools package that contain a shell bundle so we can define our custom shell commands for unomi, create a migration shell command to ease the migration to a new version


Project: http://git-wip-us.apache.org/repos/asf/incubator-unomi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-unomi/commit/f0100317
Tree: http://git-wip-us.apache.org/repos/asf/incubator-unomi/tree/f0100317
Diff: http://git-wip-us.apache.org/repos/asf/incubator-unomi/diff/f0100317

Branch: refs/heads/master
Commit: f0100317f5788aa5044f31916ae7d9ad50ebd9c8
Parents: ebfd560
Author: dgaillard <dg...@jahia.com>
Authored: Fri Sep 8 17:50:12 2017 +0200
Committer: dgaillard <dg...@jahia.com>
Committed: Thu Sep 28 10:49:15 2017 +0200

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 tools/pom.xml                                   |  37 +++++
 tools/shell-commands/pom.xml                    | 108 +++++++++++++
 .../org/apache/unomi/shell/actions/Migrate.java |  67 ++++++++
 .../org/apache/unomi/shell/actions/Version.java |  29 ++++
 .../migrations/AbstractMigrationResource.java   |  81 ++++++++++
 .../unomi/shell/migrations/MigrationTo200.java  | 130 ++++++++++++++++
 .../apache/unomi/shell/utils/ConsoleUtils.java  |  82 ++++++++++
 .../org/apache/unomi/shell/utils/HttpUtils.java | 155 +++++++++++++++++++
 .../resources/OSGI-INF/blueprint/blueprint.xml  |  32 ++++
 10 files changed, 722 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 84b09f5..fefee15 100644
--- a/pom.xml
+++ b/pom.xml
@@ -847,6 +847,7 @@
         <module>kar</module>
         <module>samples</module>
         <module>package</module>
+        <module>tools</module>
     </modules>
 
     <dependencies>

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/pom.xml
----------------------------------------------------------------------
diff --git a/tools/pom.xml b/tools/pom.xml
new file mode 100644
index 0000000..9a42616
--- /dev/null
+++ b/tools/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.unomi</groupId>
+        <artifactId>unomi-root</artifactId>
+        <version>1.2.0-incubating-unomi_117-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>unomi-tools</artifactId>
+    <name>Apache Unomi :: Tools</name>
+    <description>Apache Unomi Context Server tools</description>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>shell-commands</module>
+    </modules>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/pom.xml
----------------------------------------------------------------------
diff --git a/tools/shell-commands/pom.xml b/tools/shell-commands/pom.xml
new file mode 100644
index 0000000..51f1f06
--- /dev/null
+++ b/tools/shell-commands/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>unomi-tools</artifactId>
+        <groupId>org.apache.unomi</groupId>
+        <version>1.2.0-incubating-unomi_117-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>shell-commands</artifactId>
+    <name>Apache Unomi :: Tools :: Shell commands</name>
+    <description>Provides the shell commands to interact with Apache Unomi Context Server</description>
+    <version>1.2.0-incubating-unomi_117-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+            <version>3.0.8</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient-osgi</artifactId>
+            <version>4.5.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20160212</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>5.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <version>5.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.5.4</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Export-Package>org.apache.unomi*;version=${project.version}</Export-Package>
+                        <Import-Package>!org.apache.unomi*,
+                            org.apache.aries.blueprint,
+                            org.osgi.service.blueprint.container,
+                            org.osgi.service.blueprint.reflect,
+                            org.apache.felix.service.command,
+                            org.apache.karaf.shell.commands,
+                            org.apache.karaf.shell.console,
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Migrate.java
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Migrate.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Migrate.java
new file mode 100644
index 0000000..f24a884
--- /dev/null
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Migrate.java
@@ -0,0 +1,67 @@
+/*
+ * 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.actions;
+
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.unomi.shell.migrations.MigrationTo200;
+import org.apache.unomi.shell.utils.ConsoleUtils;
+import org.osgi.framework.Version;
+
+import java.util.*;
+
+@Command(scope = "unomi", name = "migrate", description = "This will Migrate your date in ES to be compliant with current version")
+public class Migrate extends OsgiCommandSupport {
+
+    @Argument(name = "fromVersionWithoutSuffix", description = "Origin version without suffix/qualifier (e.g: 1.2.0)", required = true, multiValued = false, valueToShowInHelp = "1.2.0")
+    private String fromVersionWithoutSuffix;
+
+    protected Object doExecute() throws Exception {
+        String confirmation = 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"));
+        if (confirmation.equalsIgnoreCase("no")) {
+            System.out.println("Migration process aborted");
+            return null;
+        }
+
+        System.out.println("Starting migration process from version: " + fromVersionWithoutSuffix);
+
+        Version fromVersion = new Version(fromVersionWithoutSuffix);
+        Version currentVersion = getCurrentVersionWithoutQualifier();
+        System.out.println("current version: " + currentVersion.toString());
+        if (currentVersion.compareTo(fromVersion) <= 0) {
+            System.out.println("From version is same or superior than current version, nothing to migrate.");
+            return null;
+        }
+
+        if (fromVersion.compareTo(new Version("2.0.0")) < 0) {
+            System.out.println("Starting migration to version 2.0.0");
+
+            MigrationTo200 migrationTo200 = new MigrationTo200(session);
+            migrationTo200.execute();
+
+            System.out.println("Migration to version 2.0.0 done successfully");
+        }
+
+        return null;
+    }
+
+    private Version getCurrentVersionWithoutQualifier() {
+        Version currentVersion = bundleContext.getBundle().getVersion();
+        return new Version(currentVersion.getMajor() + "." + currentVersion.getMinor() + "." + currentVersion.getMicro());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Version.java
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Version.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Version.java
new file mode 100644
index 0000000..50661e0
--- /dev/null
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/actions/Version.java
@@ -0,0 +1,29 @@
+/*
+ * 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.actions;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+
+@Command(scope = "unomi", name = "version", description = "This will print Apache Unomi current version")
+public class Version extends OsgiCommandSupport {
+
+    protected Object doExecute() throws Exception {
+         System.out.println("Apache Unomi version: " + bundleContext.getBundle().getVersion().toString());
+         return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/AbstractMigrationResource.java
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/AbstractMigrationResource.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/AbstractMigrationResource.java
new file mode 100644
index 0000000..37171a0
--- /dev/null
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/AbstractMigrationResource.java
@@ -0,0 +1,81 @@
+/*
+ * 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.migrations;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.http.HttpEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.apache.unomi.shell.utils.ConsoleUtils;
+import org.apache.unomi.shell.utils.HttpUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author dgaillard
+ */
+public abstract class AbstractMigrationResource {
+    protected CloseableHttpClient httpClient;
+    protected CommandSession session;
+
+    protected AbstractMigrationResource(CommandSession session) throws IOException {
+        this.session = session;
+    }
+
+    protected void initHttpClient() throws IOException {
+        if (httpClient == null) {
+            String confirmation = ConsoleUtils.askUserWithAuthorizedAnswer(session,"We need to initialize a HttpClient, do we need to trust all certificates? (yes/no):", Arrays.asList("yes", "no"));
+            httpClient = HttpUtils.initHttpClient(confirmation.equalsIgnoreCase("yes"));
+        }
+    }
+
+    protected void closeHttpClient() throws IOException {
+        if (httpClient != null) {
+            httpClient.close();
+        }
+    }
+
+    protected List<JSONArray> getDataToMigrate(String url, int offset, int size) throws IOException {
+        String jsonData = "{\"query\":{\"bool\":{\"should\":[{\"match_all\":{}}]}},\"from\":" + offset + ",\"size\":" + size + "}";
+
+        HttpEntity entity = HttpUtils.executePostRequest(httpClient, url, jsonData, null);
+        JSONObject responseJSON = new JSONObject(EntityUtils.toString(entity));
+        EntityUtils.consumeQuietly(entity);
+
+        List<JSONArray> totalHits = new ArrayList<>();
+        if (responseJSON.has("hits")) {
+            JSONObject hitsObject = responseJSON.getJSONObject("hits");
+            JSONArray hits = hitsObject.getJSONArray("hits");
+            totalHits.add(hits);
+            int newOffset = size + offset;
+            if (newOffset <= hitsObject.getInt("total")) {
+                totalHits.addAll(getDataToMigrate(url, newOffset, size));
+            }
+        }
+
+        return totalHits;
+    }
+
+    protected void bulkUpdate(String url, String jsonData) throws IOException {
+        HttpEntity entity = HttpUtils.executePostRequest(httpClient, url, jsonData, null);
+        JSONObject responseJSON = new JSONObject(EntityUtils.toString(entity));
+        EntityUtils.consumeQuietly(entity);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/MigrationTo200.java
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/MigrationTo200.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/MigrationTo200.java
new file mode 100644
index 0000000..134db02
--- /dev/null
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migrations/MigrationTo200.java
@@ -0,0 +1,130 @@
+/*
+ * 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.migrations;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.unomi.shell.utils.ConsoleUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author dgaillard
+ */
+public class MigrationTo200 extends AbstractMigrationResource {
+
+    public MigrationTo200(CommandSession session) throws IOException {
+        super(session);
+    }
+
+    public void execute() throws IOException {
+        try {
+            initHttpClient();
+            migrateTags();
+        } catch (IOException e) {
+            closeHttpClient();
+            throw e;
+        }
+    }
+
+    private void migrateTags() throws IOException {
+        String hostAddress = ConsoleUtils.askUserWithDefaultAnswer(session, "Host address (default = http://localhost:9200): ", "http://localhost:9200");
+
+        List<JSONArray> totalHits = getDataToMigrate(hostAddress + "/context/propertyType/_search", 0, 10);
+
+        StringBuilder updatedHits = new StringBuilder();
+        for (JSONArray hits : totalHits) {
+            Iterator<Object> hitsIterator = hits.iterator();
+            LinkedHashMap<String, List<String>> legacyTags = getTagsStructurePriorTo200();
+            while (hitsIterator.hasNext()) {
+                JSONObject hit = (JSONObject) hitsIterator.next();
+                if (hit.has("_source")) {
+                    JSONObject hitSource = hit.getJSONObject("_source");
+                    if (hitSource.has("tags")) {
+                        JSONArray hitTags = hitSource.getJSONArray("tags");
+                        Iterator<Object> tagsIterator = hitTags.iterator();
+                        List<String> tagsBeforeMigration = new ArrayList<>();
+                        List<String> tagsAfterMigration = new ArrayList<>();
+                        while (tagsIterator.hasNext()) {
+                            tagsBeforeMigration.add((String) tagsIterator.next());
+                        }
+                        for (String tag : tagsBeforeMigration) {
+                            if (legacyTags.containsKey(tag) && !tagsAfterMigration.containsAll(legacyTags.get(tag))) {
+                                tagsAfterMigration.addAll(legacyTags.get(tag));
+                            }
+
+                            if (!tagsAfterMigration.contains(tag)) {
+                                tagsAfterMigration.add(tag);
+                            }
+                        }
+
+                        updatedHits.append("{\"update\":{\"_id\":\"").append(hit.getString("_id")).append("\"}}\n");
+                        updatedHits.append("{\"doc\":{\"tags\":").append(new JSONArray(tagsAfterMigration)).append("}}\n");
+                    }
+                }
+            }
+        }
+        bulkUpdate(hostAddress + "/context/propertyType/_bulk", updatedHits.toString());
+    }
+
+    private LinkedHashMap<String, List<String>> getTagsStructurePriorTo200() {
+        LinkedHashMap<String, List<String>> tags = new LinkedHashMap<>();
+        tags.put("landing", Collections.singletonList("campaign"));
+        tags.put("parameter", Collections.singletonList("campaign"));
+        tags.put("referrer", Collections.singletonList("campaign"));
+
+        tags.put("eventCondition", Collections.singletonList("condition"));
+        tags.put("profileCondition", Collections.singletonList("condition"));
+        tags.put("sessionCondition", Collections.singletonList("condition"));
+        tags.put("sourceEventCondition", Collections.singletonList("condition"));
+        tags.put("trackedCondition", Collections.singletonList("condition"));
+        tags.put("usableInPastEventCondition", Collections.singletonList("condition"));
+
+        tags.put("formMappingRule", Collections.<String>emptyList());
+
+        tags.put("downloadGoal", Collections.singletonList("goal"));
+        tags.put("formGoal", Collections.singletonList("goal"));
+        tags.put("funnelGoal", Collections.singletonList("goal"));
+        tags.put("landingPageGoal", Collections.singletonList("goal"));
+        tags.put("pageVisitGoal", Collections.singletonList("goal"));
+        tags.put("videoGoal", Collections.singletonList("goal"));
+
+        tags.put("aggregated", Collections.singletonList("profileTags"));
+        tags.put("autocompleted", Collections.singletonList("profileTags"));
+        tags.put("demographic", Collections.singletonList("profileTags"));
+        tags.put("event", Collections.singletonList("profileTags"));
+        tags.put("geographic", Collections.singletonList("profileTags"));
+        tags.put("logical", Collections.singletonList("profileTags"));
+
+        tags.put("profileProperties", Collections.singletonList("properties"));
+        tags.put("systemProfileProperties", Arrays.asList("properties", "profileProperties"));
+        tags.put("basicProfileProperties", Arrays.asList("properties", "profileProperties"));
+        tags.put("leadProfileProperties", Arrays.asList("properties", "profileProperties"));
+        tags.put("contactProfileProperties", Arrays.asList("properties", "profileProperties"));
+        tags.put("socialProfileProperties", Arrays.asList("properties", "profileProperties"));
+        tags.put("personalProfileProperties", Arrays.asList("properties", "profileProperties"));
+        tags.put("workProfileProperties", Arrays.asList("properties", "profileProperties"));
+
+        tags.put("sessionProperties", Collections.singletonList("properties"));
+        tags.put("geographicSessionProperties", Arrays.asList("properties", "sessionProperties"));
+        tags.put("technicalSessionProperties", Arrays.asList("properties", "sessionProperties"));
+
+        return tags;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/ConsoleUtils.java
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/ConsoleUtils.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/ConsoleUtils.java
new file mode 100644
index 0000000..b2c6323
--- /dev/null
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/ConsoleUtils.java
@@ -0,0 +1,82 @@
+/*
+ * 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.utils;
+
+import jline.console.ConsoleReader;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.felix.service.command.CommandSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author dgaillard
+ */
+public class ConsoleUtils {
+    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+
+    /**
+     * This will ask a question to the user and return the default answer if the user does not answer.
+     *
+     * @param session           CommandSession
+     * @param msg               String message to ask
+     * @param defaultAnswer     String default answer
+     * @return the user answer
+     * @throws IOException
+     */
+    public static String askUserWithDefaultAnswer(CommandSession session, String msg, String defaultAnswer) throws IOException {
+        String answer = promptMessageToUser(session, msg);
+        if (StringUtils.isBlank(answer)) {
+            return defaultAnswer;
+        }
+        return answer;
+    }
+
+    /**
+     * This method allow you to ask a question to the user.
+     * The answer is controlled before being return so the question will be ask until the user enter one the authorized answer
+     *
+     * @param session           CommandSession
+     * @param msg               String message to ask
+     * @param authorizedAnswer  Array of possible answer, all answer must be in lower case
+     * @return the user answer
+     * @throws IOException
+     */
+    public static String askUserWithAuthorizedAnswer(CommandSession session, String msg, List<String> authorizedAnswer) throws IOException {
+        String answer;
+        do {
+            answer = promptMessageToUser(session,msg);
+        } while (!authorizedAnswer.contains(answer.toLowerCase()));
+        return answer;
+    }
+
+    /**
+     * This method allow you to prompt a message to the user.
+     * No control is done on the answer provided by the user.
+     *
+     * @param session   CommandSession
+     * @param msg       String message to prompt
+     * @return the user answer
+     * @throws IOException
+     */
+    public static String promptMessageToUser(CommandSession session, String msg) throws IOException {
+        ConsoleReader reader = (ConsoleReader) session.get(".jline.reader");
+        return reader.readLine(msg, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/HttpUtils.java
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/HttpUtils.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/HttpUtils.java
new file mode 100644
index 0000000..6fd1e58
--- /dev/null
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/utils/HttpUtils.java
@@ -0,0 +1,155 @@
+/*
+ * 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.utils;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.*;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.entity.StringEntity;
+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.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+/**
+ * @author dgaillard
+ */
+public class HttpUtils {
+    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+
+    public static CloseableHttpClient initHttpClient(boolean trustAllCertificates) {
+        long requestStartTime = System.currentTimeMillis();
+
+        HttpClientBuilder httpClientBuilder = HttpClients.custom().useSystemProperties();
+
+        if (trustAllCertificates) {
+            try {
+                SSLContext sslContext = SSLContext.getInstance("SSL");
+                sslContext.init(null, new TrustManager[]{new X509TrustManager() {
+                    public X509Certificate[] getAcceptedIssuers() {
+                        return null;
+                    }
+
+                    public void checkClientTrusted(X509Certificate[] certs,
+                                                   String authType) {
+                    }
+
+                    public void checkServerTrusted(X509Certificate[] certs,
+                                                   String authType) {
+                    }
+                }}, new SecureRandom());
+
+                Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
+                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
+                        .register("https", new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER))
+                        .build();
+
+                httpClientBuilder.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
+                        .setConnectionManager(new PoolingHttpClientConnectionManager(socketFactoryRegistry));
+
+            } catch (NoSuchAlgorithmException | KeyManagementException e) {
+                logger.error("Error creating SSL Context", e);
+            }
+        } else {
+            httpClientBuilder.setConnectionManager(new PoolingHttpClientConnectionManager());
+        }
+
+        RequestConfig requestConfig = RequestConfig.custom().build();
+        httpClientBuilder.setDefaultRequestConfig(requestConfig);
+
+        if (logger.isDebugEnabled()) {
+            long totalRequestTime = System.currentTimeMillis() - requestStartTime;
+            logger.debug("Init HttpClient executed in " + totalRequestTime + "ms");
+        }
+
+        return httpClientBuilder.build();
+    }
+
+    public static HttpEntity executeGetRequest(CloseableHttpClient httpClient, String url, Map<String, String> headers) throws IOException {
+        HttpGet httpGet = new HttpGet(url);
+        httpGet.addHeader("accept", "application/json");
+
+        return getHttpEntity(httpClient, url, headers, httpGet);
+    }
+
+    public static HttpEntity executeDeleteRequest(CloseableHttpClient httpClient, String url, Map<String, String> headers) throws IOException {
+        HttpDelete httpDelete = new HttpDelete(url);
+        httpDelete.addHeader("accept", "application/json");
+
+        return getHttpEntity(httpClient, url, headers, httpDelete);
+    }
+
+    public static HttpEntity executePostRequest(CloseableHttpClient httpClient, String url, String jsonData, Map<String, String> headers) throws IOException {
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.addHeader("accept", "application/json");
+
+        if (jsonData != null) {
+            StringEntity input = new StringEntity(jsonData);
+            input.setContentType("application/json");
+            httpPost.setEntity(input);
+        }
+
+        return getHttpEntity(httpClient, url, headers, httpPost);
+    }
+
+    private static HttpEntity getHttpEntity(CloseableHttpClient httpClient, String url, Map<String, String> headers, HttpRequestBase httpRequestBase) throws IOException {
+        long requestStartTime = System.currentTimeMillis();
+        if (headers != null) {
+            for (Map.Entry<String, String> entry : headers.entrySet()) {
+                httpRequestBase.setHeader(entry.getKey(), entry.getValue());
+            }
+        }
+
+        CloseableHttpResponse response = httpClient.execute(httpRequestBase);
+        final int statusCode = response.getStatusLine().getStatusCode();
+        if (statusCode >= 400) {
+            throw new IOException("Couldn't execute " + httpRequestBase + " response: " + EntityUtils.toString(response.getEntity()));
+        }
+
+        HttpEntity entity = response.getEntity();
+        if (logger.isDebugEnabled()) {
+            if (entity !=null) {
+                entity = new BufferedHttpEntity(response.getEntity());
+            }
+            logger.debug("POST request " + httpRequestBase + " executed with code: " + statusCode + " and message: " + (entity!=null?EntityUtils.toString(entity):null));
+
+            long totalRequestTime = System.currentTimeMillis() - requestStartTime;
+            logger.debug("Request to Apache Unomi url: " + url + " executed in " + totalRequestTime + "ms");
+        }
+
+        return entity;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/f0100317/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml
new file mode 100644
index 0000000..8f1080b
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<blueprint xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+           xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xsi:schemaLocation="http://karaf.apache.org/xmlns/shell/v1.1.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.apache.unomi.shell.actions.Migrate"/>
+        </command>
+        <command>
+            <action class="org.apache.unomi.shell.actions.Version"/>
+        </command>
+    </command-bundle>
+
+</blueprint>