| /* |
| * 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.flink.table.client.cli; |
| |
| import org.apache.flink.runtime.rest.util.RestClientException; |
| import org.apache.flink.util.ExceptionUtils; |
| import org.apache.flink.util.Preconditions; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.jline.utils.AttributedString; |
| import org.jline.utils.AttributedStringBuilder; |
| import org.jline.utils.AttributedStyle; |
| |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| /** Utility class that contains all strings for CLI commands and messages. */ |
| public final class CliStrings { |
| |
| private CliStrings() { |
| // private |
| } |
| |
| public static final String CLI_NAME = "Flink SQL CLI Client"; |
| public static final String DEFAULT_MARGIN = " "; |
| |
| // -------------------------------------------------------------------------------------------- |
| |
| private static final String CMD_DESC_DELIMITER = "\t\t"; |
| |
| /** SQL Client HELP command helper class. */ |
| private static final class SQLCliCommandsDescriptions { |
| private int commandMaxLength; |
| private final Map<String, String> commandsDescriptions; |
| |
| public SQLCliCommandsDescriptions() { |
| this.commandsDescriptions = new LinkedHashMap<>(); |
| this.commandMaxLength = -1; |
| } |
| |
| public SQLCliCommandsDescriptions commandDescription(String command, String description) { |
| Preconditions.checkState( |
| StringUtils.isNotBlank(command), "content of command must not be empty."); |
| Preconditions.checkState( |
| StringUtils.isNotBlank(description), |
| "content of command's description must not be empty."); |
| this.updateMaxCommandLength(command.length()); |
| this.commandsDescriptions.put(command, description); |
| return this; |
| } |
| |
| private void updateMaxCommandLength(int newLength) { |
| Preconditions.checkState(newLength > 0); |
| if (this.commandMaxLength < newLength) { |
| this.commandMaxLength = newLength; |
| } |
| } |
| |
| public AttributedString build() { |
| AttributedStringBuilder attributedStringBuilder = new AttributedStringBuilder(); |
| if (!this.commandsDescriptions.isEmpty()) { |
| this.commandsDescriptions.forEach( |
| (cmd, cmdDesc) -> { |
| attributedStringBuilder |
| .style(AttributedStyle.DEFAULT.bold()) |
| .append( |
| String.format( |
| String.format("%%-%ds", commandMaxLength), cmd)) |
| .append(CMD_DESC_DELIMITER) |
| .style(AttributedStyle.DEFAULT) |
| .append(cmdDesc) |
| .append('\n'); |
| }); |
| } |
| return attributedStringBuilder.toAttributedString(); |
| } |
| } |
| |
| private static final AttributedString SQL_CLI_COMMANDS_DESCRIPTIONS = |
| new SQLCliCommandsDescriptions() |
| .commandDescription("HELP", "Prints the available commands.") |
| .commandDescription("QUIT/EXIT", "Quits the SQL CLI client.") |
| .commandDescription("CLEAR", "Clears the current terminal.") |
| .commandDescription( |
| "SET", |
| "Sets a session configuration property. Syntax: \"SET '<key>'='<value>';\". Use \"SET;\" for listing all properties.") |
| .commandDescription( |
| "RESET", |
| "Resets a session configuration property. Syntax: \"RESET '<key>';\". Use \"RESET;\" for reset all session properties.") |
| .commandDescription( |
| "INSERT INTO", |
| "Inserts the results of a SQL SELECT query into a declared table sink.") |
| .commandDescription( |
| "INSERT OVERWRITE", |
| "Inserts the results of a SQL SELECT query into a declared table sink and overwrite existing data.") |
| .commandDescription( |
| "SELECT", "Executes a SQL SELECT query on the Flink cluster.") |
| .commandDescription( |
| "EXPLAIN", |
| "Describes the execution plan of a query or table with the given name.") |
| .commandDescription( |
| "BEGIN STATEMENT SET", |
| "Begins a statement set. Syntax: \"BEGIN STATEMENT SET;\"") |
| .commandDescription("END", "Ends a statement set. Syntax: \"END;\"") |
| .commandDescription( |
| "ADD JAR", |
| "Adds the specified jar file to the submitted jobs' classloader. Syntax: \"ADD JAR '<path_to_filename>.jar'\"") |
| .commandDescription( |
| "REMOVE JAR", |
| "Removes the specified jar file from the submitted jobs' classloader. Syntax: \"REMOVE JAR '<path_to_filename>.jar'\"") |
| .commandDescription( |
| "SHOW JARS", |
| "Shows the list of user-specified jar dependencies. This list is impacted by the --jar and --library startup options as well as the ADD/REMOVE JAR commands.") |
| .build(); |
| |
| // -------------------------------------------------------------------------------------------- |
| |
| public static final AttributedString MESSAGE_HELP = |
| new AttributedStringBuilder() |
| .append("The following commands are available:\n\n") |
| .append(SQL_CLI_COMMANDS_DESCRIPTIONS) |
| .style(AttributedStyle.DEFAULT.underline()) |
| .append("\nHint") |
| .style(AttributedStyle.DEFAULT) |
| .append( |
| ": Make sure that a statement ends with \";\" for finalizing (multi-line) statements.") |
| // About Documentation Link. |
| .style(AttributedStyle.DEFAULT) |
| .append( |
| "\nYou can also type any Flink SQL statement, please visit https://nightlies.apache.org/flink/flink-docs-stable/docs/dev/table/sql/overview/ for more details.") |
| .toAttributedString(); |
| |
| public static final String MESSAGE_WELCOME; |
| |
| // make findbugs happy |
| static { |
| MESSAGE_WELCOME = |
| " \u2592\u2593\u2588\u2588\u2593\u2588\u2588\u2592\n" |
| + " \u2593\u2588\u2588\u2588\u2588\u2592\u2592\u2588\u2593\u2592\u2593\u2588\u2588\u2588\u2593\u2592\n" |
| + " \u2593\u2588\u2588\u2588\u2593\u2591\u2591 \u2592\u2592\u2592\u2593\u2588\u2588\u2592 \u2592\n" |
| + " \u2591\u2588\u2588\u2592 \u2592\u2592\u2593\u2593\u2588\u2593\u2593\u2592\u2591 \u2592\u2588\u2588\u2588\u2588\n" |
| + " \u2588\u2588\u2592 \u2591\u2592\u2593\u2588\u2588\u2588\u2592 \u2592\u2588\u2592\u2588\u2592\n" |
| + " \u2591\u2593\u2588 \u2588\u2588\u2588 \u2593\u2591\u2592\u2588\u2588\n" |
| + " \u2593\u2588 \u2592\u2592\u2592\u2592\u2592\u2593\u2588\u2588\u2593\u2591\u2592\u2591\u2593\u2593\u2588\n" |
| + " \u2588\u2591 \u2588 \u2592\u2592\u2591 \u2588\u2588\u2588\u2593\u2593\u2588 \u2592\u2588\u2592\u2592\u2592\n" |
| + " \u2588\u2588\u2588\u2588\u2591 \u2592\u2593\u2588\u2593 \u2588\u2588\u2592\u2592\u2592 \u2593\u2588\u2588\u2588\u2592\n" |
| + " \u2591\u2592\u2588\u2593\u2593\u2588\u2588 \u2593\u2588\u2592 \u2593\u2588\u2592\u2593\u2588\u2588\u2593 \u2591\u2588\u2591\n" |
| + " \u2593\u2591\u2592\u2593\u2588\u2588\u2588\u2588\u2592 \u2588\u2588 \u2592\u2588 \u2588\u2593\u2591\u2592\u2588\u2592\u2591\u2592\u2588\u2592\n" |
| + " \u2588\u2588\u2588\u2593\u2591\u2588\u2588\u2593 \u2593\u2588 \u2588 \u2588\u2593 \u2592\u2593\u2588\u2593\u2593\u2588\u2592\n" |
| + " \u2591\u2588\u2588\u2593 \u2591\u2588\u2591 \u2588 \u2588\u2592 \u2592\u2588\u2588\u2588\u2588\u2588\u2593\u2592 \u2588\u2588\u2593\u2591\u2592\n" |
| + " \u2588\u2588\u2588\u2591 \u2591 \u2588\u2591 \u2593 \u2591\u2588 \u2588\u2588\u2588\u2588\u2588\u2592\u2591\u2591 \u2591\u2588\u2591\u2593 \u2593\u2591\n" |
| + " \u2588\u2588\u2593\u2588 \u2592\u2592\u2593\u2592 \u2593\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2593\u2591 \u2592\u2588\u2592 \u2592\u2593 \u2593\u2588\u2588\u2593\n" |
| + " \u2592\u2588\u2588\u2593 \u2593\u2588 \u2588\u2593\u2588 \u2591\u2592\u2588\u2588\u2588\u2588\u2588\u2593\u2593\u2592\u2591 \u2588\u2588\u2592\u2592 \u2588 \u2592 \u2593\u2588\u2592\n" |
| + " \u2593\u2588\u2593 \u2593\u2588 \u2588\u2588\u2593 \u2591\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2592 \u2592\u2588\u2588\u2593 \u2591\u2588\u2592\n" |
| + " \u2593\u2588 \u2588 \u2593\u2588\u2588\u2588\u2593\u2592\u2591 \u2591\u2593\u2593\u2593\u2588\u2588\u2588\u2593 \u2591\u2592\u2591 \u2593\u2588\n" |
| + " \u2588\u2588\u2593 \u2588\u2588\u2592 \u2591\u2592\u2593\u2593\u2588\u2588\u2588\u2593\u2593\u2593\u2593\u2593\u2588\u2588\u2588\u2588\u2588\u2588\u2593\u2592 \u2593\u2588\u2588\u2588 \u2588\n" |
| + " \u2593\u2588\u2588\u2588\u2592 \u2588\u2588\u2588 \u2591\u2593\u2593\u2592\u2591\u2591 \u2591\u2593\u2588\u2588\u2588\u2588\u2593\u2591 \u2591\u2592\u2593\u2592 \u2588\u2593\n" |
| + " \u2588\u2593\u2592\u2592\u2593\u2593\u2588\u2588 \u2591\u2592\u2592\u2591\u2591\u2591\u2592\u2592\u2592\u2592\u2593\u2588\u2588\u2593\u2591 \u2588\u2593\n" |
| + " \u2588\u2588 \u2593\u2591\u2592\u2588 \u2593\u2593\u2593\u2593\u2592\u2591\u2591 \u2592\u2588\u2593 \u2592\u2593\u2593\u2588\u2588\u2593 \u2593\u2592 \u2592\u2592\u2593\n" |
| + " \u2593\u2588\u2593 \u2593\u2592\u2588 \u2588\u2593\u2591 \u2591\u2592\u2593\u2593\u2588\u2588\u2592 \u2591\u2593\u2588\u2592 \u2592\u2592\u2592\u2591\u2592\u2592\u2593\u2588\u2588\u2588\u2588\u2588\u2592\n" |
| + " \u2588\u2588\u2591 \u2593\u2588\u2592\u2588\u2592 \u2592\u2593\u2593\u2592 \u2593\u2588 \u2588\u2591 \u2591\u2591\u2591\u2591 \u2591\u2588\u2592\n" |
| + " \u2593\u2588 \u2592\u2588\u2593 \u2591 \u2588\u2591 \u2592\u2588 \u2588\u2593\n" |
| + " \u2588\u2593 \u2588\u2588 \u2588\u2591 \u2593\u2593 \u2592\u2588\u2593\u2593\u2593\u2592\u2588\u2591\n" |
| + " \u2588\u2593 \u2591\u2593\u2588\u2588\u2591 \u2593\u2592 \u2593\u2588\u2593\u2592\u2591\u2591\u2591\u2592\u2593\u2588\u2591 \u2592\u2588\n" |
| + " \u2588\u2588 \u2593\u2588\u2593\u2591 \u2592 \u2591\u2592\u2588\u2592\u2588\u2588\u2592 \u2593\u2593\n" |
| + " \u2593\u2588\u2592 \u2592\u2588\u2593\u2592\u2591 \u2592\u2592 \u2588\u2592\u2588\u2593\u2592\u2592\u2591\u2591\u2592\u2588\u2588\n" |
| + " \u2591\u2588\u2588\u2592 \u2592\u2593\u2593\u2592 \u2593\u2588\u2588\u2593\u2592\u2588\u2592 \u2591\u2593\u2593\u2593\u2593\u2592\u2588\u2593\n" |
| + " \u2591\u2593\u2588\u2588\u2592 \u2593\u2591 \u2592\u2588\u2593\u2588 \u2591\u2591\u2592\u2592\u2592\n" |
| + " \u2592\u2593\u2593\u2593\u2593\u2593\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2592\u2591\u2591\u2593\u2593 \u2593\u2591\u2592\u2588\u2591\n" |
| + " \n" |
| + " ______ _ _ _ _____ ____ _ _____ _ _ _ BETA \n" |
| + " | ____| (_) | | / ____|/ __ \\| | / ____| (_) | | \n" |
| + " | |__ | |_ _ __ | | __ | (___ | | | | | | | | |_ ___ _ __ | |_ \n" |
| + " | __| | | | '_ \\| |/ / \\___ \\| | | | | | | | | |/ _ \\ '_ \\| __|\n" |
| + " | | | | | | | | < ____) | |__| | |____ | |____| | | __/ | | | |_ \n" |
| + " |_| |_|_|_| |_|_|\\_\\ |_____/ \\___\\_\\______| \\_____|_|_|\\___|_| |_|\\__|\n" |
| + " \n" |
| + " Welcome! Enter 'HELP;' to list all available commands. 'QUIT;' to exit.\n\n"; |
| } |
| |
| public static final String MESSAGE_QUIT = "Exiting " + CliStrings.CLI_NAME + "..."; |
| |
| public static final String MESSAGE_SQL_EXECUTION_ERROR = "Could not execute SQL statement."; |
| |
| public static final String MESSAGE_RESULT_QUIT = "Result retrieval cancelled."; |
| |
| public static final String MESSAGE_SUBMITTING_STATEMENT = |
| "Submitting SQL update statement to the cluster..."; |
| |
| public static final String MESSAGE_FINISH_STATEMENT = |
| "Complete execution of the SQL update statement."; |
| |
| public static final String MESSAGE_STATEMENT_SUBMITTED = |
| "SQL update statement has been successfully submitted to the cluster:"; |
| |
| public static final String MESSAGE_EXECUTE_FILE = "Executing SQL from file."; |
| |
| public static final String MESSAGE_EXECUTE_STATEMENT = "Execute statement succeed."; |
| |
| // -------------------------------------------------------------------------------------------- |
| |
| public static final String RESULT_TITLE = "SQL Query Result"; |
| |
| public static final String RESULT_REFRESH_INTERVAL = "Refresh:"; |
| |
| public static final String RESULT_PAGE = "Page:"; |
| |
| public static final String RESULT_PAGE_OF = " of "; |
| |
| public static final String RESULT_LAST_REFRESH = "Updated:"; |
| |
| public static final String RESULT_LAST_PAGE = "Last"; |
| |
| public static final String RESULT_QUIT = "Quit"; |
| |
| public static final String RESULT_REFRESH = "Refresh"; |
| |
| public static final String RESULT_GOTO = "Goto Page"; |
| |
| public static final String RESULT_NEXT = "Next Page"; |
| |
| public static final String RESULT_PREV = "Prev Page"; |
| |
| public static final String RESULT_LAST = "Last Page"; |
| |
| public static final String RESULT_FIRST = "First Page"; |
| |
| public static final String RESULT_SEARCH = "Search"; |
| |
| public static final String RESULT_INC_REFRESH = |
| "Inc Refresh"; // implementation assumes max length of 11 |
| |
| public static final String RESULT_DEC_REFRESH = "Dec Refresh"; |
| |
| public static final String RESULT_OPEN = "Open Row"; |
| |
| public static final String RESULT_CHANGELOG = "Changelog"; |
| |
| public static final String RESULT_TABLE = "Table"; |
| |
| public static final String RESULT_STOPPED = "Table program finished."; |
| |
| public static final String RESULT_REFRESH_UNKNOWN = "Unknown"; |
| |
| // -------------------------------------------------------------------------------------------- |
| |
| public static final String INPUT_TITLE = "Input Dialog"; |
| |
| public static final AttributedString INPUT_HELP = |
| new AttributedStringBuilder() |
| .append("Press ") |
| .style(AttributedStyle.DEFAULT.inverse()) |
| .append("Enter") |
| .style(AttributedStyle.DEFAULT) |
| .append(" to submit. Press ") |
| .style(AttributedStyle.DEFAULT.inverse()) |
| .append("ESC") |
| .style(AttributedStyle.DEFAULT) |
| .append(" or submit an empty string to cancel.") |
| .toAttributedString(); |
| |
| public static final String INPUT_ENTER_PAGE = "Enter page number:"; |
| |
| public static final String INPUT_ERROR = "The input is invalid please check it again."; |
| |
| // -------------------------------------------------------------------------------------------- |
| |
| public static final AttributedString ROW_QUIT = |
| new AttributedStringBuilder() |
| .append("Press ") |
| .style(AttributedStyle.DEFAULT.inverse()) |
| .append("Q") |
| .style(AttributedStyle.DEFAULT) |
| .append(" to go back.") |
| .toAttributedString(); |
| |
| public static final String ROW_HEADER = "Row Summary"; |
| |
| // -------------------------------------------------------------------------------------------- |
| |
| public static AttributedString messageInfo(String message) { |
| return new AttributedStringBuilder() |
| .style(AttributedStyle.DEFAULT.bold().foreground(AttributedStyle.BLUE)) |
| .append("[INFO] ") |
| .append(message) |
| .toAttributedString(); |
| } |
| |
| public static AttributedString messageWarning(String message) { |
| return new AttributedStringBuilder() |
| .style(AttributedStyle.DEFAULT.bold().foreground(AttributedStyle.YELLOW)) |
| .append("[WARNING] ") |
| .append(message) |
| .toAttributedString(); |
| } |
| |
| public static AttributedString messageError(String message, Throwable t, boolean isVerbose) { |
| while (t.getCause() != null |
| && t.getCause().getMessage() != null |
| && !t.getCause().getMessage().isEmpty()) { |
| t = t.getCause(); |
| } |
| |
| if (isVerbose) { |
| return messageError(message, ExceptionUtils.stringifyException(t)); |
| } else { |
| if (t instanceof RestClientException) { |
| // TODO: Remove this after RestClientException supports to get RootCause. |
| String[] splitExceptions = t.getMessage().split("Caused by: "); |
| return messageError( |
| message, |
| splitExceptions[splitExceptions.length - 1].split("\tat ")[0].trim()); |
| } else { |
| return messageError(message, t.getClass().getName() + ": " + t.getMessage()); |
| } |
| } |
| } |
| |
| public static AttributedString messageError(String message) { |
| return messageError(message, (String) null); |
| } |
| |
| public static AttributedString messageError(String message, String s) { |
| final AttributedStringBuilder builder = |
| new AttributedStringBuilder() |
| .style(AttributedStyle.DEFAULT.bold().foreground(AttributedStyle.RED)) |
| .append("[ERROR] ") |
| .append(message); |
| |
| if (s != null) { |
| builder.append(" Reason:\n").append(s); |
| } |
| |
| return builder.toAttributedString(); |
| } |
| } |