Repository: incubator-unomi Updated Branches: refs/heads/feature-UNOMI-117 2730d9367 -> 960178014
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/96017801 Tree: http://git-wip-us.apache.org/repos/asf/incubator-unomi/tree/96017801 Diff: http://git-wip-us.apache.org/repos/asf/incubator-unomi/diff/96017801 Branch: refs/heads/feature-UNOMI-117 Commit: 960178014cf3b3192bf4195d9eeea2ce61eaf90e Parents: 2730d93 Author: dgaillard <[email protected]> Authored: Fri Sep 8 17:50:12 2017 +0200 Committer: dgaillard <[email protected]> Committed: Fri Sep 8 17:50:12 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/96017801/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 3f1b056..32234f6 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/96017801/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/96017801/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/96017801/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/96017801/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/96017801/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/96017801/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/96017801/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/96017801/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/96017801/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>
