| /* |
| * Copyright 2017 HugeGraph Authors |
| * |
| * 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 com.baidu.hugegraph.cmd; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.io.FileUtils; |
| |
| import com.baidu.hugegraph.api.API; |
| import com.baidu.hugegraph.manager.TasksManager; |
| import com.baidu.hugegraph.structure.constant.GraphMode; |
| import com.baidu.hugegraph.structure.constant.HugeType; |
| import com.baidu.hugegraph.util.E; |
| import com.beust.jcommander.DynamicParameter; |
| import com.beust.jcommander.IParameterValidator; |
| import com.beust.jcommander.IStringConverter; |
| import com.beust.jcommander.Parameter; |
| import com.beust.jcommander.ParameterException; |
| import com.beust.jcommander.Parameters; |
| import com.beust.jcommander.ParametersDelegate; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| |
| public class SubCommands { |
| |
| private Map<String, Object> commands; |
| |
| public SubCommands() { |
| this.commands = new HashMap<>(); |
| this.initSubCommands(); |
| } |
| |
| private void initSubCommands() { |
| this.commands.put("backup", new Backup()); |
| this.commands.put("restore", new Restore()); |
| this.commands.put("schedule-backup", new ScheduleBackup()); |
| this.commands.put("dump", new DumpGraph()); |
| this.commands.put("graph-list", new GraphList()); |
| this.commands.put("graph-get", new GraphGet()); |
| this.commands.put("graph-clear", new GraphClear()); |
| this.commands.put("graph-mode-set", new GraphModeSet()); |
| this.commands.put("graph-mode-get", new GraphModeGet()); |
| this.commands.put("gremlin-execute", new Gremlin()); |
| this.commands.put("gremlin-schedule", new GremlinJob()); |
| this.commands.put("task-list", new TaskList()); |
| this.commands.put("task-get", new TaskGet()); |
| this.commands.put("task-delete", new TaskDelete()); |
| this.commands.put("task-cancel", new TaskCancel()); |
| this.commands.put("task-clear", new TaskClear()); |
| this.commands.put("deploy", new Deploy()); |
| this.commands.put("start-all", new StartAll()); |
| this.commands.put("clear", new Clear()); |
| this.commands.put("stop-all", new StopAll()); |
| this.commands.put("help", new Help()); |
| } |
| |
| public Map<String, Object> commands() { |
| return this.commands; |
| } |
| |
| @Parameters(commandDescription = "Schedule backup task") |
| public class ScheduleBackup { |
| |
| @Parameter(names = {"--interval"}, arity = 1, |
| description = |
| "The interval of backup, format is: \"a b c d e\"." + |
| " 'a' means minute (0 - 59)," + |
| " 'b' means hour (0 - 23)," + |
| " 'c' means day of month (1 - 31)," + |
| " 'd' means month (1 - 12)," + |
| " 'e' means day of week (0 - 6) (Sunday=0)," + |
| " \"*\" means all") |
| public String interval = "0,0,*,*,*"; |
| |
| @Parameter(names = {"--backup-num"}, arity = 1, |
| description = "The number of latest backups to keep") |
| public int num = 3; |
| |
| @Parameter(names = {"--directory", "-d"}, arity = 1, required = true, |
| description = "The directory of backups stored") |
| public String directory; |
| } |
| |
| @Parameters(commandDescription = "Backup graph schema/data. If directory " + |
| "is on HDFS, use -D to set HDFS params " + |
| "if needed. For exmaple:" + |
| "-Dfs.default.name=hdfs://localhost:9000") |
| public class Backup extends BackupRestore { |
| |
| @Parameter(names = {"--split-size", "-s"}, arity = 1, |
| description = "Split size of shard") |
| public long splitSize = 1024 * 1024L; |
| |
| public long splitSize() { |
| return this.splitSize; |
| } |
| } |
| |
| @Parameters(commandDescription = "Restore graph schema/data. If directory" + |
| " is on HDFS, use -D to set HDFS params " + |
| "if needed. For exmaple:" + |
| "-Dfs.default.name=hdfs://localhost:9000") |
| public class Restore extends BackupRestore {} |
| |
| @Parameters(commandDescription = "Dump graph to files") |
| public class DumpGraph { |
| |
| @ParametersDelegate |
| private Retry retry = new Retry(); |
| |
| @Parameter(names = {"--directory", "-d"}, arity = 1, |
| description = "Directory to store graph data") |
| public String directory = "./"; |
| |
| @Parameter(names = {"--formatter", "-f"}, arity = 1, |
| description = "Formatter to customize format of vertex/edge") |
| public String formatter = "JsonFormatter"; |
| |
| public int retry() { |
| return this.retry.retry; |
| } |
| |
| public String directory() { |
| return this.directory; |
| } |
| |
| public String formatter() { |
| return this.formatter; |
| } |
| } |
| |
| @Parameters(commandDescription = "List all graphs") |
| public class GraphList { |
| } |
| |
| @Parameters(commandDescription = "Get graph info") |
| public class GraphGet { |
| |
| @ParametersDelegate |
| private GraphName graph = new GraphName(); |
| |
| public String graph() { |
| return this.graph.graphName; |
| } |
| } |
| |
| @Parameters(commandDescription = "Clear graph schema and data") |
| public class GraphClear { |
| |
| @ParametersDelegate |
| private GraphName graph = new GraphName(); |
| |
| @ParametersDelegate |
| private ConfirmMessage message = new ConfirmMessage(); |
| |
| public String graph() { |
| return this.graph.graphName; |
| } |
| |
| public String confirmMessage() { |
| return this.message.confirmMessage; |
| } |
| } |
| |
| @Parameters(commandDescription = "Set graph mode") |
| public class GraphModeSet { |
| |
| @ParametersDelegate |
| private GraphName graph = new GraphName(); |
| |
| @ParametersDelegate |
| private Mode mode = new Mode(); |
| |
| public String graph() { |
| return this.graph.graphName; |
| } |
| |
| public GraphMode mode() { |
| return this.mode.mode; |
| } |
| } |
| |
| @Parameters(commandDescription = "Get graph mode") |
| public class GraphModeGet { |
| |
| @ParametersDelegate |
| private GraphName graph = new GraphName(); |
| |
| public String graph() { |
| return this.graph.graphName; |
| } |
| } |
| |
| @Parameters(commandDescription = "Execute Gremlin statements") |
| public class Gremlin extends GremlinJob { |
| |
| @ParametersDelegate |
| private Aliases aliases = new Aliases(); |
| |
| public Map<String, String> aliases() { |
| return this.aliases.aliases; |
| } |
| } |
| |
| @Parameters(commandDescription = "Execute Gremlin statements as " + |
| "asynchronous job") |
| public class GremlinJob { |
| |
| @ParametersDelegate |
| private FileScript fileScript = new FileScript(); |
| |
| @ParametersDelegate |
| private GremlinScript cmdScript = new GremlinScript(); |
| |
| @ParametersDelegate |
| private Language language = new Language(); |
| |
| @ParametersDelegate |
| private Bindings bindings = new Bindings(); |
| |
| public String fileScript() { |
| return this.fileScript.script; |
| } |
| |
| public String cmdScript() { |
| return this.cmdScript.script; |
| } |
| |
| public String language() { |
| return this.language.language; |
| } |
| |
| public Map<String, String> bindings() { |
| return this.bindings.bindings; |
| } |
| |
| public String script() { |
| String script; |
| if (this.cmdScript() == null) { |
| E.checkArgument(this.fileScript() != null, |
| "Either --script or --file must " + |
| "be provided, but both are null"); |
| script = this.fileScript(); |
| } else { |
| E.checkArgument(this.fileScript() == null, |
| "Either --script or --file must " + |
| "be provided, but both are provided: " + |
| "'%s' and '%s'", |
| this.cmdScript(), this.fileScript()); |
| script = this.cmdScript(); |
| } |
| return script; |
| } |
| } |
| |
| @Parameters(commandDescription = "List tasks") |
| public class TaskList { |
| |
| @ParametersDelegate |
| private TaskStatus status = new TaskStatus(); |
| |
| @ParametersDelegate |
| private Limit limit = new Limit(); |
| |
| public String status() { |
| return this.status.status.toUpperCase(); |
| } |
| |
| public long limit() { |
| return this.limit.limit; |
| } |
| } |
| |
| @Parameters(commandDescription = "Get task info") |
| public class TaskGet { |
| |
| @ParametersDelegate |
| private TaskId taskId = new TaskId(); |
| |
| public long taskId() { |
| return this.taskId.taskId; |
| } |
| } |
| |
| @Parameters(commandDescription = "Delete task") |
| public class TaskDelete { |
| |
| @ParametersDelegate |
| private TaskId taskId = new TaskId(); |
| |
| public long taskId() { |
| return this.taskId.taskId; |
| } |
| } |
| |
| @Parameters(commandDescription = "Cancel task") |
| public class TaskCancel { |
| |
| @ParametersDelegate |
| private TaskId taskId = new TaskId(); |
| |
| public long taskId() { |
| return this.taskId.taskId; |
| } |
| } |
| |
| @Parameters(commandDescription = "Clear completed tasks") |
| public class TaskClear { |
| |
| @Parameter(names = "--force", |
| description = "Force to clear all tasks, " + |
| "cancel all uncompleted tasks firstly, " + |
| "and delete all completed task") |
| private boolean force = false; |
| |
| public boolean force() { |
| return this.force; |
| } |
| } |
| |
| @Parameters(commandDescription = "Install HugeGraph-Server and " + |
| "HugeGraph-Studio") |
| public class Deploy { |
| |
| @ParametersDelegate |
| private Version version = new Version(); |
| |
| @ParametersDelegate |
| public InstallPath path = new InstallPath(); |
| |
| @ParametersDelegate |
| public DownloadURL url = new DownloadURL(); |
| } |
| |
| @Parameters(commandDescription = "Start HugeGraph-Server and " + |
| "HugeGraph-Studio") |
| public class StartAll { |
| |
| @ParametersDelegate |
| private Version version = new Version(); |
| |
| @ParametersDelegate |
| public InstallPath path = new InstallPath(); |
| } |
| |
| @Parameters(commandDescription = "Clear HugeGraph-Server and " + |
| "HugeGraph-Studio") |
| public class Clear { |
| |
| @ParametersDelegate |
| public InstallPath path = new InstallPath(); |
| } |
| |
| @Parameters(commandDescription = "Stop HugeGraph-Server and " + |
| "HugeGraph-Studio") |
| public class StopAll { |
| } |
| |
| @Parameters(commandDescription = "Print usage") |
| public class Help { |
| } |
| |
| public class BackupRestore { |
| |
| @Parameter(names = {"--directory", "-d"}, arity = 1, |
| description = "Directory of graph schema/data") |
| public String directory = "./"; |
| |
| @Parameter(names = {"--log", "-l"}, arity = 1, |
| description = "Directory of log") |
| public String logDir = "./"; |
| |
| @ParametersDelegate |
| private HugeTypes types = new HugeTypes(); |
| |
| @ParametersDelegate |
| private Retry retry = new Retry(); |
| |
| @DynamicParameter(names = "-D", |
| description = "HDFS config parameters") |
| private Map<String, String> hdfsConf = new HashMap<>(); |
| |
| public String directory() { |
| return this.directory; |
| } |
| |
| public String logDir() { |
| return this.logDir; |
| } |
| |
| public List<HugeType> types() { |
| return this.types.types; |
| } |
| |
| public int retry() { |
| return this.retry.retry; |
| } |
| |
| public Map<String, String> hdfsConf() { |
| return this.hdfsConf; |
| } |
| } |
| |
| public static class Url { |
| |
| @Parameter(names = {"--url"}, arity = 1, |
| validateWith = {UrlValidator.class}, |
| description = "The URL of HugeGraph-Server url") |
| public String url = "http://127.0.0.1:8080"; |
| } |
| |
| public static class Graph { |
| |
| @Parameter(names = {"--graph"}, arity = 1, |
| description = "Name of graph") |
| public String graph = "hugegraph"; |
| } |
| |
| public static class Username { |
| |
| @Parameter(names = {"--user"}, arity = 1, |
| description = "User Name") |
| public String username; |
| } |
| |
| public static class Password { |
| |
| @Parameter(names = {"--password"}, arity = 1, |
| description = "Password of user") |
| public String password; |
| } |
| |
| public static class Timeout { |
| |
| @Parameter(names = {"--timeout"}, arity = 1, |
| description = "Connection timeout") |
| public int timeout = 30; |
| } |
| |
| public static class GraphName { |
| |
| @Parameter(names = {"--graph-name"}, arity = 1, |
| description = "Name of graph") |
| public String graphName = "hugegraph"; |
| } |
| |
| public class HugeTypes { |
| |
| @Parameter(names = {"--huge-types", "-t"}, |
| listConverter = HugeTypeListConverter.class, |
| required = true, |
| description = "Type of schema/data. " + |
| "Concat with ',' if more than one. " + |
| "'all' means 'vertex,edge,vertex_label," + |
| "edge_label,property_key,index_label'") |
| public List<HugeType> types; |
| } |
| |
| public class ExistDirectory { |
| |
| @Parameter(names = {"--directory", "-d"}, arity = 1, |
| validateWith = {DirectoryValidator.class}, |
| description = "Directory of graph schema/data") |
| public String directory = "./"; |
| } |
| |
| public class InstallPath { |
| |
| @Parameter(names = {"-p"}, arity = 1, required = true, |
| description = "Install path of HugeGraph-Server and " + |
| "HugeGraph-Studio") |
| public String directory = null; |
| } |
| |
| public class DownloadURL { |
| |
| @Parameter(names = {"-u"}, arity = 1, |
| description = "Download url prefix path of " + |
| "HugeGraph-Server and HugeGraph-Studio") |
| public String url = null; |
| } |
| |
| public class ConfirmMessage { |
| |
| @Parameter(names = {"--confirm-message", "-c"}, arity = 1, |
| description = "Confirm message of graph clear is " + |
| "\"I'm sure to delete all data\". " + |
| "(Note: include \"\")", |
| required = true) |
| public String confirmMessage; |
| } |
| |
| public class Mode { |
| |
| @Parameter(names = {"--graph-mode", "-m"}, arity = 1, |
| converter = GraphModeConverter.class, |
| description = "Graph mode, " + |
| "include: [NONE, RESTORING, MERGING]", |
| required = true) |
| public GraphMode mode; |
| } |
| |
| public class FileScript { |
| |
| @Parameter(names = {"--file", "-f"}, arity = 1, |
| converter = FileNameToContentConverter.class, |
| description = "Gremlin Script file to be executed, UTF-8" + |
| "encoded, exclusive to --script") |
| public String script; |
| } |
| |
| public class GremlinScript { |
| |
| @Parameter(names = {"--script", "-s"}, arity = 1, |
| description = "Gremlin script to be executed, " + |
| "exclusive to --file") |
| public String script; |
| } |
| |
| public class Language { |
| |
| @Parameter(names = {"--language", "-l"}, arity = 1, |
| description = "Gremlin script language") |
| public String language = "gremlin-groovy"; |
| } |
| |
| public class Bindings { |
| |
| @Parameter(names = {"--bindings", "-b"}, arity = 1, |
| converter = MapConverter.class, |
| description = "Gremlin bindings, valid format is: " + |
| "'key1=value1,key2=value2...'") |
| public Map<String, String> bindings = ImmutableMap.of(); |
| } |
| |
| public class Aliases { |
| |
| @Parameter(names = {"--aliases", "-a"}, arity = 1, |
| converter = MapConverter.class, |
| description = "Gremlin aliases, valid format is: " + |
| "'key1=value1,key2=value2...'") |
| public Map<String, String> aliases = ImmutableMap.of(); |
| } |
| |
| public class Version { |
| |
| @Parameter(names = {"-v"}, arity = 1, required = true, |
| description = "Version of HugeGraph-Server and " + |
| "HugeGraph-Studio") |
| public String version; |
| } |
| |
| public class Retry { |
| |
| @Parameter(names = {"--retry"}, arity = 1, |
| validateWith = {PositiveValidator.class}, |
| description = "Retry times, default is 3") |
| public int retry = 3; |
| } |
| |
| public class Limit { |
| |
| @Parameter(names = {"--limit"}, arity = 1, |
| validateWith = {PositiveValidator.class}, |
| description = "Limit number, no limit if not provided") |
| public long limit = -1; |
| } |
| |
| public class TaskStatus { |
| |
| @Parameter(names = {"--status"}, arity = 1, |
| validateWith = TaskStatusValidator.class, |
| description = "Status of task") |
| public String status = null; |
| } |
| |
| public class TaskId { |
| |
| @Parameter(names = {"--task-id"}, arity = 1, required = true, |
| validateWith = {PositiveValidator.class}, |
| description = "Task id") |
| private long taskId; |
| } |
| |
| public static class GraphModeConverter |
| implements IStringConverter<GraphMode> { |
| |
| @Override |
| public GraphMode convert(String value) { |
| E.checkArgument(value != null && !value.isEmpty(), |
| "GraphMode can't be null or empty"); |
| return GraphMode.valueOf(value); |
| } |
| } |
| |
| public static class HugeTypeListConverter |
| implements IStringConverter<List<HugeType>> { |
| |
| @Override |
| public List<HugeType> convert(String value) { |
| E.checkArgument(value != null && !value.isEmpty(), |
| "HugeType can't be null or empty"); |
| String[] types = value.split(","); |
| if (types.length == 1 && types[0].equalsIgnoreCase("all")) { |
| return ImmutableList.of(HugeType.PROPERTY_KEY, |
| HugeType.VERTEX_LABEL, |
| HugeType.EDGE_LABEL, |
| HugeType.INDEX_LABEL, |
| HugeType.VERTEX, |
| HugeType.EDGE); |
| } |
| List<HugeType> hugeTypes = new ArrayList<>(); |
| for (String type : types) { |
| try { |
| hugeTypes.add(HugeType.valueOf(type.toUpperCase())); |
| } catch (IllegalArgumentException e) { |
| throw new ParameterException(String.format( |
| "Invalid --type '%s', valid value is 'all' or " + |
| "combination of 'vertex,edge,vertex_label," + |
| "edge_label,property_key,index_label'", type)); |
| } |
| } |
| return hugeTypes; |
| } |
| } |
| |
| public static class MapConverter |
| implements IStringConverter<Map<String, String>> { |
| |
| @Override |
| public Map<String, String> convert(String value) { |
| E.checkArgument(value != null && !value.isEmpty(), |
| "HugeType can't be null or empty"); |
| String[] equals = value.split(","); |
| Map<String, String> result = new HashMap<>(); |
| for (String equal : equals) { |
| String[] kv = equal.split("="); |
| E.checkArgument(kv.length == 2, |
| "Map arguments format should be key=value, " + |
| "but got '%s'", equal); |
| result.put(kv[0], kv[1]); |
| } |
| return result; |
| } |
| } |
| |
| public static class FileNameToContentConverter |
| implements IStringConverter<String> { |
| |
| @Override |
| public String convert(String value) { |
| File file = FileUtils.getFile(value); |
| if (!file.exists() || !file.isFile() || !file.canRead()) { |
| throw new ParameterException(String.format( |
| "'%s' must be existed readable file", value)); |
| } |
| String content; |
| try { |
| content = FileUtils.readFileToString(file, API.CHARSET); |
| } catch (IOException e) { |
| throw new ParameterException(String.format( |
| "Read file '%s' error", value), e); |
| } |
| return content; |
| } |
| } |
| |
| public static class TaskStatusValidator implements IParameterValidator { |
| |
| @Override |
| public void validate(String name, String value) { |
| if (!TasksManager.TASK_STATUSES.contains(value.toUpperCase())) { |
| throw new ParameterException(String.format( |
| "Invalid --status '%s', valid value is %s", |
| value, TasksManager.TASK_STATUSES)); |
| } |
| } |
| } |
| |
| public static class UrlValidator implements IParameterValidator { |
| |
| @Override |
| public void validate(String name, String value) { |
| String regex = "^(http://)?" |
| // IP URL, like: 10.0.0.1 |
| + "(((25[0-5])|(2[0-4]\\d)|(1\\d\\d)|([1-9]\\d)|\\d)" |
| + "(\\.((25[0-5])|(2[0-4]\\d)|(1\\d\\d)|([1-9]\\d)|\\d)){3}" |
| + "|" |
| // Or domain name |
| + "([0-9a-z_!~*'()-]+\\.)*[0-9a-z_!~*'()-]+)" |
| // Port |
| + ":([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-5]{2}[0-3][0-5])$"; |
| if (!value.matches(regex)) { |
| throw new ParameterException(String.format( |
| "Invalid url value of args '%s': '%s'", name, value)); |
| } |
| } |
| } |
| |
| public static class DirectoryValidator implements IParameterValidator { |
| |
| @Override |
| public void validate(String name, String value) { |
| File file = new File(value); |
| if (!file.exists() || !file.isDirectory()) { |
| throw new ParameterException(String.format( |
| "Invalid value of argument '%s': '%s'", name, value)); |
| } |
| } |
| } |
| |
| public static class PositiveValidator implements IParameterValidator { |
| |
| @Override |
| public void validate(String name, String value) { |
| int retry = Integer.parseInt(value); |
| if (retry <= 0) { |
| throw new ParameterException( |
| "Parameter " + name + " should be positive, " + |
| "but got " + value); |
| } |
| } |
| } |
| } |