You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2022/06/06 19:28:38 UTC
[ignite-3] branch main updated: IGNITE-16971 Command line interface with REPL support (MVP)
This is an automated email from the ASF dual-hosted git repository.
agura pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 56059d2d5 IGNITE-16971 Command line interface with REPL support (MVP)
56059d2d5 is described below
commit 56059d2d57d46f74079a858f541b30cb50fb1f93
Author: Mikhail Pochatkin <mp...@unison.team>
AuthorDate: Mon Jun 6 22:23:09 2022 +0300
IGNITE-16971 Command line interface with REPL support (MVP)
Co-authored-by: Aleksandr Pakhomov <ap...@unison.team>
Signed-off-by: Andrey Gura <ag...@apache.org>
---
assembly/assembly.xml | 5 +
.../ignite/example/rebalance/RebalanceExample.java | 2 +-
.../ignite/example/sql/jdbc/SqlJdbcExample.java | 2 +-
.../storage/VolatilePageMemoryStorageExample.java | 2 +-
.../ignite/example/table/KeyValueViewExample.java | 2 +-
.../example/table/KeyValueViewPojoExample.java | 2 +-
.../ignite/example/table/RecordViewExample.java | 2 +-
.../example/table/RecordViewPojoExample.java | 2 +-
.../ignite/example/tx/TransactionsExample.java | 2 +-
modules/cli/DEVNOTES.md | 25 +
modules/cli/README.md | 37 --
modules/cli/ignite.bat | 16 +
modules/cli/pom.xml | 312 +++++++++---
.../org/apache/ignite/cli/IntegrationTestBase.java | 344 +++++++++++++
.../ignite/cli/call/CallIntegrationTestBase.java} | 10 +-
.../configuration/ItShowConfigurationCallTest.java | 151 ++++++
.../ItUpdateConfigurationCallTest.java | 78 +++
.../commands/CliCommandTestIntegrationBase.java | 133 +++++
.../configuration/ItConfigCommandTest.java | 93 ++++
.../cli/commands/connect/ItConnectCommandTest.java | 112 +++++
.../ignite/cli/commands/sql/ItSqlCommandTest.java | 85 ++++
.../commands/status/ItStatusReplCommandTest.java} | 44 +-
.../commands/topology/ItTopologyCommandTest.java | 50 ++
.../commands/version/ItVersionCommandTest.java} | 33 +-
.../AbstractCliIntegrationTest.java | 6 +-
.../cli/{ => deprecated}/ItClusterCommandTest.java | 5 +-
.../cli/{ => deprecated}/ItConfigCommandTest.java | 66 ++-
.../ignite/cli/{ => deprecated}/NoOpHandler.java | 2 +-
.../ignite/rest/ItGeneratedRestClientTest.java | 236 +++++++++
.../java/org/apache/ignite/cli/CliVersionInfo.java | 1 +
.../java/org/apache/ignite/cli/ErrorHandler.java | 66 ---
.../org/apache/ignite/cli/HelpFactoryImpl.java | 174 -------
.../java/org/apache/ignite/cli/IgniteCliApp.java | 80 ---
.../org/apache/ignite/cli/InteractiveWrapper.java | 116 -----
.../src/main/java/org/apache/ignite/cli/Main.java | 169 +++++++
.../org/apache/ignite/cli/VersionProvider.java | 7 +
.../cli/builtins/config/ConfigurationClient.java | 168 -------
.../cliconfig/CliConfigCall.java} | 32 +-
.../cliconfig/CliConfigGetCall.java} | 33 +-
.../cliconfig/CliConfigSetCall.java} | 36 +-
.../cli/call/cliconfig/CliConfigSetCallInput.java} | 22 +-
.../call/configuration/ClusterConfigShowCall.java | 58 +++
.../configuration/ClusterConfigShowCallInput.java | 87 ++++
.../configuration/ClusterConfigUpdateCall.java | 67 +++
.../ClusterConfigUpdateCallInput.java | 91 ++++
.../cli/call/configuration/NodeConfigShowCall.java | 58 +++
.../configuration/NodeConfigShowCallInput.java | 89 ++++
.../call/configuration/NodeConfigUpdateCall.java | 67 +++
.../configuration/NodeConfigUpdateCallInput.java | 91 ++++
.../ignite/cli/call/connect/ConnectCall.java | 86 ++++
.../connect/ConnectCallInput.java} | 49 +-
.../connect/DisconnectCall.java} | 41 +-
.../org/apache/ignite/cli/call/status/Status.java | 117 +++++
.../apache/ignite/cli/call/status/StatusCall.java | 99 ++++
.../ignite/cli/call/status/StatusReplCall.java | 86 ++++
.../CommandSpec.java => commands/BaseCommand.java} | 19 +-
.../ignite/cli/commands/TopLevelCliCommand.java | 60 +++
.../cli/commands/TopLevelCliReplCommand.java | 53 ++
.../cliconfig/CliCommand.java} | 19 +-
.../cliconfig/CliConfigGetSubCommand.java} | 42 +-
.../commands/cliconfig/CliConfigSetSubCommand.java | 49 ++
.../commands/cliconfig/CliConfigSubCommand.java | 52 ++
.../configuration/cluster/ClusterCommand.java} | 24 +-
.../cluster/ClusterConfigReplSubCommand.java} | 15 +-
.../cluster/ClusterConfigShowReplSubCommand.java | 100 ++++
.../cluster/ClusterConfigShowSubCommand.java | 72 +++
.../cluster/ClusterConfigSubCommand.java} | 15 +-
.../cluster/ClusterConfigUpdateReplSubCommand.java | 79 +++
.../cluster/ClusterConfigUpdateSubCommand.java | 72 +++
.../configuration/cluster/ClusterReplCommand.java} | 18 +-
.../configuration/node/NodeCommand.java} | 19 +-
.../node/NodeConfigReplSubCommand.java} | 15 +-
.../node/NodeConfigShowReplSubCommand.java | 100 ++++
.../node/NodeConfigShowSubCommand.java | 72 +++
.../configuration/node/NodeConfigSubCommand.java} | 19 +-
.../node/NodeConfigUpdateReplSubCommand.java | 79 +++
.../node/NodeConfigUpdateSubCommand.java | 70 +++
.../configuration/node/NodeReplCommand.java} | 18 +-
.../cli/commands/connect/ConnectCommand.java | 63 +++
.../connect/DisconnectCommand.java} | 40 +-
.../cli/commands/decorators/ConfigDecorator.java | 42 ++
.../cli/commands/decorators/DefaultDecorator.java} | 21 +-
.../decorators/JsonDecorator.java} | 41 +-
.../decorators/SqlQueryResultDecorator.java} | 25 +-
.../decorators/StatusDecorator.java} | 40 +-
.../commands/decorators/StatusReplDecorator.java | 40 ++
.../decorators/TableDecorator.java} | 34 +-
.../decorators/core/Decorator.java} | 27 +-
.../decorators/core/TerminalOutput.java} | 14 +-
.../apache/ignite/cli/commands/sql/SqlCommand.java | 125 +++++
.../ignite/cli/commands/sql/SqlCompleter.java | 72 +++
.../ignite/cli/commands/sql/SqlMetaData.java | 96 ++++
.../sql/SqlReplTopLevelCliCommand.java} | 19 +-
.../ignite/cli/commands/status/StatusCommand.java | 68 +++
.../cli/commands/status/StatusReplCommand.java | 65 +++
.../cli/commands/topology/TopologyCommand.java | 53 ++
.../version/VersionCommand.java} | 31 +-
.../java/org/apache/ignite/cli/config/Config.java | 123 +++++
.../ConfigFactory.java} | 16 +-
.../CallExecutionPipelineProvider.java} | 28 +-
.../{spec/CommandSpec.java => core/call/Call.java} | 16 +-
.../cli/core/call/CallExecutionPipeline.java | 183 +++++++
.../package-info.java => core/call/CallInput.java} | 9 +-
.../call/CallOutput.java} | 43 +-
.../call/CallOutputStatus.java} | 9 +-
.../ignite/cli/core/call/DefaultCallOutput.java | 187 +++++++
.../call/EmptyCallInput.java} | 8 +-
.../ignite/cli/core/call/StatusCallInput.java} | 22 +-
.../ignite/cli/core/call/StringCallInput.java} | 23 +-
.../exception/CommandExecutionException.java} | 42 +-
.../core/exception/ConnectCommandException.java} | 29 +-
.../cli/core/exception/ExceptionHandler.java | 58 +++
.../cli/core/exception/ExceptionHandlers.java | 79 +++
.../exception/ExceptionWriter.java} | 31 +-
.../cli/core/exception/WrappedException.java} | 18 +-
.../exception/handler/ApiExceptionHandler.java} | 19 +-
.../handler/CommandExecutionExceptionHandler.java | 41 ++
.../handler/ConnectCommandExceptionHandler.java} | 19 +-
.../handler/ConnectExceptionHandler.java} | 19 +-
.../handler/DefaultExceptionHandlers.java | 41 ++
.../handler/EndOfFileExceptionHandler.java | 55 +++
.../handler/IgniteCliExceptionHandler.java} | 19 +-
.../handler/IgniteClientExceptionHandler.java} | 23 +-
.../handler/PicocliExecutionExceptionHandler.java} | 19 +-
.../exception/handler/ReplExceptionHandlers.java} | 26 +-
.../exception/handler/SqlExceptionHandler.java | 67 +++
.../handler/TimeoutExceptionHandler.java} | 23 +-
.../handler/UnknownCommandExceptionHandler.java} | 21 +-
.../handler/UserInterruptExceptionHandler.java} | 19 +-
.../java/org/apache/ignite/cli/core/repl/Repl.java | 150 ++++++
.../apache/ignite/cli/core/repl/ReplBuilder.java | 139 ++++++
.../repl/Session.java} | 54 +-
.../cli/core/repl/SessionDefaultValueProvider.java | 57 +++
.../repl/config/ClientConnectorConfig.java} | 14 +-
.../repl/config/RootConfig.java} | 12 +-
.../repl/executor/RegistryCommandExecutor.java | 68 +++
.../cli/core/repl/executor/ReplExecutor.java | 145 ++++++
.../repl/executor/ReplExecutorProvider.java} | 34 +-
.../cli/core/repl/executor/SqlQueryCall.java | 54 ++
.../cli/core/repl/expander/NoopExpander.java} | 20 +-
.../repl/prompt/PromptProvider.java} | 12 +-
.../repl/prompt/ReplPromptProvider.java} | 32 +-
.../repl/terminal/TerminalCustomizer.java} | 17 +-
.../repl/terminal}/TerminalFactory.java | 2 +-
.../cli/{ => deprecated}/CliPathsConfigLoader.java | 9 +-
.../cli/{ => deprecated}/CommandFactory.java | 2 +-
.../cli/{ => deprecated}/IgniteCliException.java | 4 +-
.../ignite/cli/{ => deprecated}/IgnitePaths.java | 7 +-
.../apache/ignite/cli/{ => deprecated}/Table.java | 2 +-
.../builtins/SystemPathResolver.java | 8 +-
.../builtins/cluster/ClusterApiClient.java | 4 +-
.../builtins/cluster/InitClusterRequest.java | 2 +-
.../builtins/config/HttpClientFactory.java | 2 +-
.../builtins/config/package-info.java | 2 +-
.../builtins/init/InitIgniteCommand.java | 14 +-
.../builtins/init/package-info.java | 2 +-
.../builtins/module/MavenArtifactResolver.java | 8 +-
.../builtins/module/MavenCoordinates.java | 5 +-
.../builtins/module/ModuleManager.java | 6 +-
.../builtins/module/ModuleRegistry.java | 6 +-
.../builtins/module/ResolveResult.java | 2 +-
.../builtins/module/StandardModuleDefinition.java | 2 +-
.../builtins/module/package-info.java | 2 +-
.../builtins/node/NodeManager.java | 8 +-
.../builtins/node/package-info.java | 2 +-
.../{ => deprecated}/builtins/package-info.java | 2 +-
.../ignite/cli/{ => deprecated}/package-info.java | 2 +-
.../spec/BootstrapIgniteCommandSpec.java} | 11 +-
.../{ => deprecated}/spec/ClusterCommandSpec.java | 9 +-
.../spec/ClusterReplCommandSpec.java} | 36 +-
.../cli/{ => deprecated}/spec/NodeCommandSpec.java | 23 +-
.../{ => deprecated}/spec/NodeEndpointOptions.java | 4 +-
.../cli/{ => deprecated}/spec/package-info.java | 2 +-
.../cli/{ => deprecated}/ui/ProgressBar.java | 2 +-
.../ignite/cli/{ => deprecated}/ui/Spinner.java | 2 +-
.../apache/ignite/cli/spec/ConfigCommandSpec.java | 117 -----
.../org/apache/ignite/cli/spec/IgniteCliSpec.java | 142 ------
.../apache/ignite/cli/spec/ModuleCommandSpec.java | 231 ---------
.../org/apache/ignite/cli/spec/SpecAdapter.java | 64 ---
.../apache/ignite/cli/sql/MetadataSupplier.java} | 20 +-
.../package-info.java => sql/SchemaProvider.java} | 14 +-
.../java/org/apache/ignite/cli/sql/SqlManager.java | 73 +++
.../org/apache/ignite/cli/sql/SqlQueryResult.java | 71 +++
.../java/org/apache/ignite/cli/sql/SqlSchema.java | 88 ++++
.../org/apache/ignite/cli/sql/SqlSchemaLoader.java | 71 +++
.../apache/ignite/cli/sql/SqlSchemaProvider.java | 55 +++
.../org/apache/ignite/cli/sql/table/Table.java | 115 +++++
.../org/apache/ignite/cli/sql/table/TableRow.java | 71 +++
modules/cli/src/main/resources/application.yml | 21 +
.../resources/cli.java.util.logging.properties | 14 +-
.../cli/builtins/module/ModuleMangerTest.java | 162 ------
.../ignite/cli/builtins/module/package-info.java | 22 -
.../ignite/cli/commands/CliCommandTestBase.java} | 52 +-
.../cliconfig/CliConfigGetSubCommandTest.java | 81 +++
.../cliconfig/CliConfigSetSubCommandTest.java | 92 ++++
.../cliconfig/CliConfigSubCommandTest.java | 47 ++
.../ignite/cli/commands/cliconfig/ConfigTest.java} | 30 +-
.../cli/commands/cliconfig/TestConfigFactory.java} | 33 +-
.../configuration/ShowConfigSubCommandTest.java | 48 ++
.../sql/SchemaProviderMock.java} | 21 +-
.../ignite/cli/commands/sql/SqlCompleterTest.java | 63 +++
.../cli/{ => deprecated}/AbstractCliTest.java | 2 +-
.../{ => deprecated}/IgniteCliInterfaceTest.java | 544 +++++++++------------
.../builtins/init/InitIgniteCommandTest.java | 12 +-
.../builtins/init/package-info.java | 2 +-
.../ignite/cli/{ => deprecated}/package-info.java | 2 +-
.../cli/{ => deprecated}/ui/ProgressBarTest.java | 4 +-
.../cli/{ => deprecated}/ui/SpinnerTest.java | 2 +-
.../cli/{ => deprecated}/ui/package-info.java | 2 +-
.../ignite/cli/sql/SqlSchemaProviderTest.java | 59 +++
.../apache/ignite/cli/sql/table/TableTest.java} | 41 +-
.../apache/ignite/internal/jdbc/JdbcStatement.java | 30 +-
parent/pom.xml | 117 ++++-
213 files changed, 8092 insertions(+), 2726 deletions(-)
diff --git a/assembly/assembly.xml b/assembly/assembly.xml
index e10c872e0..0eb01c0bf 100644
--- a/assembly/assembly.xml
+++ b/assembly/assembly.xml
@@ -47,6 +47,11 @@
<outputDirectory/>
</file>
+ <file>
+ <source>modules/cli/target/ignite_completion.sh</source>
+ <outputDirectory/>
+ </file>
+
<file>
<source>modules/cli/target/ignite.exe</source>
<outputDirectory/>
diff --git a/examples/src/main/java/org/apache/ignite/example/rebalance/RebalanceExample.java b/examples/src/main/java/org/apache/ignite/example/rebalance/RebalanceExample.java
index e463cf0e2..4a2db3991 100644
--- a/examples/src/main/java/org/apache/ignite/example/rebalance/RebalanceExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/rebalance/RebalanceExample.java
@@ -41,7 +41,7 @@ import org.apache.ignite.table.Tuple;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start <b>two</b> nodes using the CLI tool:<br>
diff --git a/examples/src/main/java/org/apache/ignite/example/sql/jdbc/SqlJdbcExample.java b/examples/src/main/java/org/apache/ignite/example/sql/jdbc/SqlJdbcExample.java
index f873b879c..ed6e4f622 100644
--- a/examples/src/main/java/org/apache/ignite/example/sql/jdbc/SqlJdbcExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/sql/jdbc/SqlJdbcExample.java
@@ -31,7 +31,7 @@ import java.sql.Statement;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start an Ignite node using the CLI tool:<br>
diff --git a/examples/src/main/java/org/apache/ignite/example/storage/VolatilePageMemoryStorageExample.java b/examples/src/main/java/org/apache/ignite/example/storage/VolatilePageMemoryStorageExample.java
index cebd65ca5..f8ee293bd 100644
--- a/examples/src/main/java/org/apache/ignite/example/storage/VolatilePageMemoryStorageExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/storage/VolatilePageMemoryStorageExample.java
@@ -31,7 +31,7 @@ import java.sql.Statement;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start an Ignite node using the CLI tool:<br>
diff --git a/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewExample.java b/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewExample.java
index a51e0a945..508f37457 100644
--- a/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewExample.java
@@ -32,7 +32,7 @@ import org.apache.ignite.table.Tuple;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start an Ignite node using the CLI tool:<br>
diff --git a/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewPojoExample.java b/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewPojoExample.java
index 03fdc1f28..497218535 100644
--- a/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewPojoExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/table/KeyValueViewPojoExample.java
@@ -31,7 +31,7 @@ import org.apache.ignite.table.KeyValueView;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start an Ignite node using the CLI tool:<br>
diff --git a/examples/src/main/java/org/apache/ignite/example/table/RecordViewExample.java b/examples/src/main/java/org/apache/ignite/example/table/RecordViewExample.java
index 7d58366ee..eb1926dd6 100644
--- a/examples/src/main/java/org/apache/ignite/example/table/RecordViewExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/table/RecordViewExample.java
@@ -32,7 +32,7 @@ import org.apache.ignite.table.Tuple;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start an Ignite node using the CLI tool:<br>
diff --git a/examples/src/main/java/org/apache/ignite/example/table/RecordViewPojoExample.java b/examples/src/main/java/org/apache/ignite/example/table/RecordViewPojoExample.java
index c41a10a2f..df43264e4 100644
--- a/examples/src/main/java/org/apache/ignite/example/table/RecordViewPojoExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/table/RecordViewPojoExample.java
@@ -31,7 +31,7 @@ import org.apache.ignite.table.RecordView;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start an Ignite node using the CLI tool:<br>
diff --git a/examples/src/main/java/org/apache/ignite/example/tx/TransactionsExample.java b/examples/src/main/java/org/apache/ignite/example/tx/TransactionsExample.java
index c6018dd4a..f032b2c04 100644
--- a/examples/src/main/java/org/apache/ignite/example/tx/TransactionsExample.java
+++ b/examples/src/main/java/org/apache/ignite/example/tx/TransactionsExample.java
@@ -33,7 +33,7 @@ import org.apache.ignite.tx.IgniteTransactions;
* <li>Import the examples project into you IDE.</li>
* <li>
* Download and prepare artifacts for running an Ignite node using the CLI tool (if not done yet):<br>
- * {@code ignite init}
+ * {@code ignite bootstrap}
* </li>
* <li>
* Start an Ignite node using the CLI tool:<br>
diff --git a/modules/cli/DEVNOTES.md b/modules/cli/DEVNOTES.md
new file mode 100644
index 000000000..d28f5dc0d
--- /dev/null
+++ b/modules/cli/DEVNOTES.md
@@ -0,0 +1,25 @@
+# Ignite CLI DEVNOTES
+
+## How to build module and add bash/zsh autocompletion to your shell
+
+Build the ignite-3 and cli modules:
+```bash
+mvn clean install -DskipTests=true
+```
+
+Cd to the build directory:
+```bash
+cd modules/cli/target
+```
+
+Install autocompletion script to your shell:
+```bash
+source target/ignite_completion.sh
+```
+
+Add `ignite` alias:
+```bash
+alias ignite='./ignite'
+```
+
+For more info, see [Autocomplete for Java Command Line Applications](https://picocli.info/autocomplete.html).
diff --git a/modules/cli/README.md b/modules/cli/README.md
deleted file mode 100644
index f5f1e8637..000000000
--- a/modules/cli/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Ignite CLI module
-
-Ignite CLI tool is a single entry point for any management operations.
-
-## Build
-
- mvn package -f ../../pom.xml
-## Run
-For Windows:
-
- target/ignite.exe
-For Linux/MacOS:
-
- ./target/ignite
-## Examples
-Download and prepare artifacts for running an Ignite node:
-
- ignite init
-Node start:
-
- ignite start consistent-id
-Node stop:
-
- ignite stop consistent-id
-
-Cluster initialization:
-
- ignite cluster init --cluster-name=cluster-name --node-endpoint=localhost:10300 --meta-storage-node=consistent-id --cmg-node=consistent-id
-
-Get current node configuration:
-
- ignite config get --node-endpoint=localhost:10300
-Show help:
-
- ignite --help
- ignite init --help
- ...
diff --git a/modules/cli/ignite.bat b/modules/cli/ignite.bat
new file mode 100644
index 000000000..0833e9149
--- /dev/null
+++ b/modules/cli/ignite.bat
@@ -0,0 +1,16 @@
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+start java -jar target/ignite.jar
diff --git a/modules/cli/pom.xml b/modules/cli/pom.xml
index 218ccff05..07d258972 100644
--- a/modules/cli/pom.xml
+++ b/modules/cli/pom.xml
@@ -33,6 +33,21 @@
<version>3.0.0-SNAPSHOT</version>
<dependencies>
+ <dependency>
+ <groupId>com.jakewharton.fliptables</groupId>
+ <artifactId>fliptables</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jline</groupId>
+ <artifactId>jline</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jline</groupId>
+ <artifactId>jline-console</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-cli-common</artifactId>
@@ -43,30 +58,30 @@
<artifactId>ignite-core</artifactId>
</dependency>
- <!-- 3rd party dependencies -->
<dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-api</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.ivy</groupId>
- <artifactId>ivy</artifactId>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-client</artifactId>
</dependency>
+ <!-- 3rd party dependencies -->
<dependency>
- <groupId>info.picocli</groupId>
- <artifactId>picocli-shell-jline3</artifactId>
+ <groupId>io.micronaut</groupId>
+ <artifactId>micronaut-inject</artifactId>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
- <artifactId>micronaut-inject-java</artifactId>
+ <artifactId>micronaut-validation</artifactId>
</dependency>
<dependency>
- <groupId>com.typesafe</groupId>
- <artifactId>config</artifactId>
+ <groupId>info.picocli</groupId>
+ <artifactId>picocli-shell-jline3</artifactId>
</dependency>
<dependency>
@@ -80,8 +95,28 @@
</dependency>
<dependency>
- <groupId>org.jetbrains</groupId>
- <artifactId>annotations</artifactId>
+ <groupId>io.micronaut</groupId>
+ <artifactId>micronaut-runtime</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.micronaut.picocli</groupId>
+ <artifactId>micronaut-picocli</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ivy</groupId>
+ <artifactId>ivy</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe</groupId>
+ <artifactId>config</artifactId>
</dependency>
<dependency>
@@ -89,7 +124,42 @@
<artifactId>slf4j-jdk14</artifactId>
</dependency>
- <!-- Test dependencies -->
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>okhttp</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.threeten</groupId>
+ <artifactId>threetenbp</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>logging-interceptor</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.gsonfire</groupId>
+ <artifactId>gson-fire</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.swagger</groupId>
+ <artifactId>swagger-annotations</artifactId>
+ </dependency>
+
+ <!-- Test dependencies-->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
@@ -97,26 +167,26 @@
</dependency>
<dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter-api</artifactId>
+ <groupId>com.github.npathai</groupId>
+ <artifactId>hamcrest-optional</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter-engine</artifactId>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-junit-jupiter</artifactId>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-runner</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.github.npathai</groupId>
- <artifactId>hamcrest-optional</artifactId>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-schema</artifactId>
<scope>test</scope>
</dependency>
@@ -127,14 +197,26 @@
</dependency>
<dependency>
- <groupId>org.apache.ignite</groupId>
- <artifactId>ignite-api</artifactId>
+ <groupId>io.micronaut.test</groupId>
+ <artifactId>micronaut-test-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.ignite</groupId>
- <artifactId>ignite-runner</artifactId>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@@ -173,73 +255,152 @@
</resources>
<plugins>
+ <!--Disable javadoc validation for module with generated REST client.-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-shade-plugin</artifactId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.openapitools</groupId>
+ <artifactId>openapi-generator-maven-plugin</artifactId>
<executions>
<execution>
- <phase>package</phase>
<goals>
- <goal>shade</goal>
+ <goal>generate</goal>
</goals>
<configuration>
- <finalName>ignite</finalName>
- <transformers>
- <transformer
- implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
- <manifestEntries>
- <Main-Class>org.apache.ignite.cli.IgniteCliApp</Main-Class>
- </manifestEntries>
- </transformer>
- <transformer
- implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
- </transformers>
- <filters>
- <filter>
- <artifact>*:*</artifact>
- <excludes>
- <exclude>module-info.class</exclude>
- </excludes>
- </filter>
- </filters>
+ <inputSpec>${project.basedir}/../rest/openapi/openapi.yaml</inputSpec>
+ <generatorName>java</generatorName>
+ <apiPackage>org.apache.ignite.rest.client.api</apiPackage>
+ <invokerPackage>org.apache.ignite.rest.client.invoker</invokerPackage>
+ <modelPackage>org.apache.ignite.rest.client.model</modelPackage>
+ <generateModelTests>false</generateModelTests>
+ <generateApiTests>false</generateApiTests>
+ <languageSpecificPrimitives>true</languageSpecificPrimitives>
+ <configOptions>
+ <openApiNullable>false</openApiNullable>
+ <supportStreaming>false</supportStreaming>
+ </configOptions>
+ <library>okhttp-gson</library>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!--
+ Plugin that adds integration test sources and resources from integrationTest directory.
+ -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-test-source</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>add-test-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>src/integrationTest/java</source>
+ </sources>
+ </configuration>
+ </execution>
+ <execution>
+ <id>add-test-resource</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>add-test-resource</goal>
+ </goals>
+ <configuration>
+ <resources>
+ <resource>
+ <directory>src/integrationTest/resources</directory>
+ </resource>
+ </resources>
</configuration>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>io.micronaut.build</groupId>
+ <artifactId>micronaut-maven-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ </plugin>
+
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <source>11</source>
+ <target>11</target>
<!-- Uncomment to enable incremental compilation -->
<!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->
- <annotationProcessorPaths>
+ <annotationProcessorPaths combine.children="append">
+ <path>
+ <groupId>info.picocli</groupId>
+ <artifactId>picocli-codegen</artifactId>
+ <version>${picocli.version}</version>
+ </path>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.version}</version>
</path>
<path>
- <groupId>info.picocli</groupId>
- <artifactId>picocli-codegen</artifactId>
- <version>${picocli.version}</version>
+ <groupId>io.micronaut</groupId>
+ <artifactId>micronaut-validation</artifactId>
+ <version>${micronaut.version}</version>
</path>
</annotationProcessorPaths>
+ <compilerArgs>
+ <arg>-Amicronaut.processing.group=org.apache.ignite.cli</arg>
+ <arg>-Amicronaut.processing.artifactId=${project.artifactId}</arg>
+ </compilerArgs>
</configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
- <id>test-compile</id>
+ <id>default-shade</id>
+ <phase>package</phase>
<goals>
- <goal>testCompile</goal>
+ <goal>shade</goal>
</goals>
<configuration>
- <annotationProcessorPaths>
- <path>
- <groupId>io.micronaut</groupId>
- <artifactId>micronaut-inject-java</artifactId>
- <version>${micronaut.version}</version>
- </path>
- </annotationProcessorPaths>
+ <finalName>ignite</finalName>
+ <createDependencyReducedPom>false</createDependencyReducedPom>
+ <transformers>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <manifestEntries>
+ <Main-Class>org.apache.ignite.cli.Main</Main-Class>
+ </manifestEntries>
+ </transformer>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
+ </transformers>
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>module-info.class</exclude>
+ </excludes>
+ </filter>
+ </filters>
</configuration>
</execution>
</executions>
@@ -255,7 +416,7 @@
<target>
<concat destfile="${project.build.directory}/ignite" binary="true">
<filelist dir="${project.build.directory}/"
- files="../ignite.sh,ignite.jar"/>
+ files="../ignite.sh,ignite.jar"/>
</concat>
<chmod file="${project.build.directory}/ignite" perm="+x"/>
</target>
@@ -297,6 +458,35 @@
<skip>true</skip>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-autocompletion-script</id>
+ <phase>package</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <mainClass>picocli.AutoComplete</mainClass>
+ <systemProperties>
+ <systemProperty>
+ <key>picocli.autocomplete.systemExitOnError</key>
+ </systemProperty>
+ </systemProperties>
+ <arguments>
+ <argument>--force</argument><!-- overwrite if exists -->
+ <argument>--completionScript</argument>
+ <argument>${project.build.directory}/ignite_completion.sh</argument>
+ <argument>-n</argument>
+ <argument>ignite</argument>
+ <argument>org.apache.ignite.cli.commands.TopLevelCliCommand</argument>
+ </arguments>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/IntegrationTestBase.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/IntegrationTestBase.java
new file mode 100644
index 000000000..b9e9a5274
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/IntegrationTestBase.java
@@ -0,0 +1,344 @@
+/*
+ * 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.ignite.cli;
+
+import static java.util.stream.Collectors.toList;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
+import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.IntStream;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgnitionManager;
+import org.apache.ignite.internal.app.IgniteImpl;
+import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
+import org.apache.ignite.internal.sql.engine.AsyncCursor;
+import org.apache.ignite.internal.sql.engine.AsyncCursor.BatchedResult;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.testframework.WorkDirectory;
+import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.lang.IgniteLogger;
+import org.apache.ignite.lang.IgniteStringFormatter;
+import org.apache.ignite.schema.SchemaBuilders;
+import org.apache.ignite.schema.definition.ColumnType;
+import org.apache.ignite.schema.definition.TableDefinition;
+import org.apache.ignite.schema.definition.builder.TableDefinitionBuilder;
+import org.apache.ignite.table.RecordView;
+import org.apache.ignite.table.Table;
+import org.apache.ignite.table.Tuple;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * Integration test base. Setups ignite cluster per test class and provides useful fixtures and assertions.
+ */
+@ExtendWith(WorkDirectoryExtension.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@MicronautTest
+public class IntegrationTestBase extends BaseIgniteAbstractTest {
+ public static final int DEFAULT_NODES_COUNT = 3;
+
+ /** Correct ignite cluster url. */
+ protected static final String NODE_URL = "http://localhost:10300";
+
+ /** Cluster nodes. */
+ protected static final List<Ignite> CLUSTER_NODES = new ArrayList<>();
+
+ private static final IgniteLogger LOG = IgniteLogger.forClass(IntegrationTestBase.class);
+
+ /** Base port number. */
+ private static final int BASE_PORT = 3344;
+
+ /** Nodes bootstrap configuration pattern. */
+ private static final String NODE_BOOTSTRAP_CFG = "{\n"
+ + " \"network\": {\n"
+ + " \"port\":{},\n"
+ + " \"portRange\": 5,\n"
+ + " \"nodeFinder\":{\n"
+ + " \"netClusterNodes\": [ {} ]\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+
+ /** Work directory. */
+ @WorkDirectory
+ private static Path WORK_DIR;
+
+ protected static Table createAndPopulateTable() {
+ TableDefinition schTbl1 = SchemaBuilders.tableBuilder("PUBLIC", "PERSON").columns(
+ SchemaBuilders.column("ID", ColumnType.INT32).build(),
+ SchemaBuilders.column("NAME", ColumnType.string()).asNullable(true).build(),
+ SchemaBuilders.column("SALARY", ColumnType.DOUBLE).asNullable(true).build()
+ ).withPrimaryKey("ID").build();
+
+ Table tbl = CLUSTER_NODES.get(0).tables().createTable(schTbl1.canonicalName(), tblCh ->
+ SchemaConfigurationConverter.convert(schTbl1, tblCh)
+ .changeReplicas(1)
+ .changePartitions(10)
+ );
+
+ int idx = 0;
+
+ insertData(tbl, new String[]{"ID", "NAME", "SALARY"}, new Object[][]{
+ {idx++, "Igor", 10d},
+ {idx++, null, 15d},
+ {idx++, "Ilya", 15d},
+ {idx++, "Roma", 10d},
+ {idx, "Roma", 10d}
+ });
+
+ return tbl;
+ }
+
+ protected static void createTable(TableDefinitionBuilder tblBld) {
+ TableDefinition schTbl1 = tblBld.build();
+
+ CLUSTER_NODES.get(0).tables().createTable(schTbl1.canonicalName(), tblCh ->
+ SchemaConfigurationConverter.convert(schTbl1, tblCh)
+ .changeReplicas(1)
+ .changePartitions(10)
+ );
+ }
+
+ protected static Table table(String canonicalName) {
+ return CLUSTER_NODES.get(0).tables().table(canonicalName);
+ }
+
+ protected static void insertData(String tblName, String[] columnNames, Object[]... tuples) {
+ insertData(CLUSTER_NODES.get(0).tables().table(tblName), columnNames, tuples);
+ }
+
+ protected static void insertData(Table table, String[] columnNames, Object[]... tuples) {
+ RecordView<Tuple> view = table.recordView();
+
+ int batchSize = 128;
+
+ List<Tuple> batch = new ArrayList<>(batchSize);
+ for (Object[] tuple : tuples) {
+ assert tuple != null && tuple.length == columnNames.length;
+
+ Tuple toInsert = Tuple.create();
+
+ for (int i = 0; i < tuple.length; i++) {
+ toInsert.set(columnNames[i], tuple[i]);
+ }
+
+ batch.add(toInsert);
+
+ if (batch.size() == batchSize) {
+ Collection<Tuple> duplicates = view.insertAll(null, batch);
+
+ if (!duplicates.isEmpty()) {
+ throw new AssertionError("Duplicated rows detected: " + duplicates);
+ }
+
+ batch.clear();
+ }
+ }
+
+ if (!batch.isEmpty()) {
+ view.insertAll(null, batch);
+
+ batch.clear();
+ }
+ }
+
+ protected static void checkData(Table table, String[] columnNames, Object[]... tuples) {
+ RecordView<Tuple> view = table.recordView();
+
+ for (Object[] tuple : tuples) {
+ assert tuple != null && tuple.length == columnNames.length;
+
+ Object id = tuple[0];
+
+ assert id != null : "Primary key cannot be null";
+
+ Tuple row = view.get(null, Tuple.create().set(columnNames[0], id));
+
+ assertNotNull(row);
+
+ for (int i = 0; i < columnNames.length; i++) {
+ assertEquals(tuple[i], row.value(columnNames[i]));
+ }
+ }
+ }
+
+ protected static List<List<Object>> sql(String sql, Object... args) {
+ return getAllFromCursor(
+ ((IgniteImpl) CLUSTER_NODES.get(0)).queryEngine().queryAsync("PUBLIC", sql, args).get(0).join()
+ );
+ }
+
+ private static <T> List<T> reverse(List<T> lst) {
+ List<T> res = new ArrayList<>(lst);
+
+ Collections.reverse(res);
+
+ return res;
+ }
+
+ private static <T> List<T> getAllFromCursor(AsyncCursor<T> cur) {
+ List<T> res = new ArrayList<>();
+ int batchSize = 256;
+
+ var consumer = new Consumer<BatchedResult<T>>() {
+ @Override
+ public void accept(BatchedResult<T> br) {
+ res.addAll(br.items());
+
+ if (br.hasMore()) {
+ cur.requestNextAsync(batchSize).thenAccept(this);
+ }
+ }
+ };
+
+ await(cur.requestNextAsync(batchSize).thenAccept(consumer));
+ await(cur.closeAsync());
+
+ return res;
+ }
+
+ /**
+ * Before all.
+ *
+ * @param testInfo Test information oject.
+ */
+ @BeforeAll
+ void startNodes(TestInfo testInfo) throws ExecutionException, InterruptedException {
+ String connectNodeAddr = "\"localhost:" + BASE_PORT + '\"';
+
+ List<CompletableFuture<Ignite>> futures = IntStream.range(0, nodes())
+ .mapToObj(i -> {
+ String nodeName = testNodeName(testInfo, i);
+
+ String config = IgniteStringFormatter.format(NODE_BOOTSTRAP_CFG, BASE_PORT + i, connectNodeAddr);
+
+ return IgnitionManager.start(nodeName, config, WORK_DIR.resolve(nodeName));
+ })
+ .collect(toList());
+
+ String metaStorageNodeName = testNodeName(testInfo, 0);
+
+ IgnitionManager.init(metaStorageNodeName, List.of(metaStorageNodeName), "cluster");
+
+ for (CompletableFuture<Ignite> future : futures) {
+ assertThat(future, willCompleteSuccessfully());
+
+ CLUSTER_NODES.add(future.join());
+ }
+ }
+
+ /**
+ * Get a count of nodes in the Ignite cluster.
+ *
+ * @return Count of nodes.
+ */
+ protected int nodes() {
+ return DEFAULT_NODES_COUNT;
+ }
+
+ /**
+ * After all.
+ */
+ @AfterAll
+ void stopNodes(TestInfo testInfo) throws Exception {
+ LOG.info("Start tearDown()");
+
+ CLUSTER_NODES.clear();
+
+ List<AutoCloseable> closeables = IntStream.range(0, nodes())
+ .mapToObj(i -> testNodeName(testInfo, i))
+ .map(nodeName -> (AutoCloseable) () -> IgnitionManager.stop(nodeName))
+ .collect(toList());
+
+ IgniteUtils.closeAll(closeables);
+
+ LOG.info("End tearDown()");
+ }
+
+ /** Drops all visible tables. */
+ protected void dropAllTables() {
+ for (Table t : CLUSTER_NODES.get(0).tables().tables()) {
+ sql("DROP TABLE " + t.name());
+ }
+ }
+
+ protected static PrintWriter output(List<Character> buffer) {
+ return new PrintWriter(new Writer() {
+ @Override
+ public void write(char[] cbuf, int off, int len) {
+ for (int i = off; i < off + len; i++) {
+ buffer.add(cbuf[i]);
+ }
+ }
+
+ @Override
+ public void flush() {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+ });
+ }
+
+ /**
+ * Invokes before the test will start.
+ *
+ * @param testInfo Test information object.
+ * @throws Exception If failed.
+ */
+ @BeforeEach
+ public void setUp(TestInfo testInfo) throws Exception {
+ setupBase(testInfo, WORK_DIR);
+ }
+
+ /**
+ * Invokes after the test has finished.
+ *
+ * @param testInfo Test information object.
+ */
+ @AfterEach
+ public void tearDown(TestInfo testInfo) {
+ tearDownBase(testInfo);
+ dropAllTables();
+ }
+}
+
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/CallIntegrationTestBase.java
similarity index 76%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
copy to modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/CallIntegrationTestBase.java
index 5cb93f287..d5c0f37b3 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/CallIntegrationTestBase.java
@@ -15,8 +15,12 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.call;
+
+import org.apache.ignite.cli.IntegrationTestBase;
+
/**
- * Contains classes for Ignite module management.
+ * Base class for call integration tests. Contains common methods and useful assertions.
*/
-
-package org.apache.ignite.cli.builtins.module;
+public class CallIntegrationTestBase extends IntegrationTestBase {
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItShowConfigurationCallTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItShowConfigurationCallTest.java
new file mode 100644
index 000000000..aa66ccd0b
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItShowConfigurationCallTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.common.primitives.Chars;
+import jakarta.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.cli.call.CallIntegrationTestBase;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.exception.handler.CommandExecutionExceptionHandler;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link NodeConfigShowCall}.
+ */
+class ItShowConfigurationCallTest extends CallIntegrationTestBase {
+
+ @Inject
+ NodeConfigShowCall nodeConfigShowCall;
+
+ @Inject
+ ClusterConfigShowCall clusterConfigShowCall;
+
+ @Test
+ @DisplayName("Should show cluster configuration when cluster up and running")
+ void readClusterConfiguration() {
+ // Given
+ var input = ClusterConfigShowCallInput.builder()
+ .clusterUrl(NODE_URL)
+ .build();
+
+ // When
+ DefaultCallOutput<String> output = clusterConfigShowCall.execute(input);
+
+ // Then
+ assertThat(output.hasError()).isFalse();
+ // And
+ assertThat(output.body()).isNotEmpty();
+ }
+
+ @Test
+ @DisplayName("Should show cluster configuration by path when cluster up and running")
+ void readClusterConfigurationByPath() {
+ // Given
+ var input = ClusterConfigShowCallInput.builder()
+ .clusterUrl(NODE_URL)
+ .selector("rocksDb.defaultRegion.cache")
+ .build();
+
+ // When
+ DefaultCallOutput<String> output = clusterConfigShowCall.execute(input);
+
+ // Then
+ assertThat(output.hasError()).isFalse();
+ // And
+ assertThat(output.body()).isEqualTo("\"lru\"");
+ }
+
+ @Test
+ @DisplayName("Should show node configuration when cluster up and running")
+ void readNodeConfiguration() {
+ // Given
+ var input = NodeConfigShowCallInput.builder()
+ .nodeUrl(NODE_URL)
+ .build();
+
+ // When
+ DefaultCallOutput<String> output = nodeConfigShowCall.execute(input);
+
+ // Then
+ assertThat(output.hasError()).isFalse();
+ // And
+ assertThat(output.body()).isNotEmpty();
+ }
+
+ @Test
+ @DisplayName("Should show node configuration by path when cluster up and running")
+ void readNodeConfigurationByPath() {
+ // Given
+ var input = NodeConfigShowCallInput.builder()
+ .nodeUrl(NODE_URL)
+ .selector("clientConnector.connectTimeout")
+ .build();
+
+ // When
+ DefaultCallOutput<String> output = nodeConfigShowCall.execute(input);
+
+ // Then
+ assertThat(output.hasError()).isFalse();
+ // And
+ assertThat(output.body()).isEqualTo("5000");
+ }
+
+ @Test
+ @DisplayName("Should display error when wrong port is given")
+ public void incorrectPortTest() {
+ var input = NodeConfigShowCallInput.builder()
+ .nodeUrl(NODE_URL + "incorrect")
+ .build();
+ List<Character> list = new ArrayList<>();
+
+ CallExecutionPipeline.builder(nodeConfigShowCall)
+ .inputProvider(() -> input)
+ .exceptionHandler(new CommandExecutionExceptionHandler())
+ .errOutput(output(list))
+ .build()
+ .runPipeline();
+
+ assertThat(new String(Chars.toArray(list)))
+ .contains("Invalid URL port: \"10300incorrect");
+ }
+
+ @Test
+ @DisplayName("Should display error when wrong url is given")
+ public void incorrectSchemeTest() {
+ var input = NodeConfigShowCallInput.builder()
+ .nodeUrl("incorrect" + NODE_URL)
+ .build();
+ List<Character> list = new ArrayList<>();
+
+ CallExecutionPipeline.builder(nodeConfigShowCall)
+ .inputProvider(() -> input)
+ .exceptionHandler(new CommandExecutionExceptionHandler())
+ .errOutput(output(list))
+ .build()
+ .runPipeline();
+
+ assertThat(new String(Chars.toArray(list)))
+ .contains("Expected URL scheme 'http' or 'https' but was 'incorrecthttp'");
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItUpdateConfigurationCallTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItUpdateConfigurationCallTest.java
new file mode 100644
index 000000000..46055736b
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItUpdateConfigurationCallTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.call.CallIntegrationTestBase;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link NodeConfigUpdateCall}.
+ */
+public class ItUpdateConfigurationCallTest extends CallIntegrationTestBase {
+
+ @Inject
+ ClusterConfigUpdateCall updateCall;
+
+ @Inject
+ ClusterConfigShowCall readCall;
+
+ @Test
+ @DisplayName("Should update cluster configuration")
+ void shouldUpdateClusterConfiguration() {
+ // Given default write buffer size
+ String givenConfigurationProperty = readConfigurationProperty("rocksDb.defaultRegion.writeBufferSize");
+ assertThat(givenConfigurationProperty).isEqualTo("67108864");
+ // And
+ var input = ClusterConfigUpdateCallInput.builder()
+ .clusterUrl(NODE_URL)
+ .config("{rocksDb: {defaultRegion: {writeBufferSize: 1024}}}")
+ .build();
+
+ // When update buffer size
+ DefaultCallOutput<String> output = updateCall.execute(input);
+
+ // Then
+ assertThat(output.hasError()).isFalse();
+ // And
+ assertThat(output.body()).contains("Cluster configuration was updated successfully.");
+ // And buffer size is updated
+ String updatedConfigurationProperty = readConfigurationProperty("rocksDb.defaultRegion.writeBufferSize");
+ assertThat(updatedConfigurationProperty).isEqualTo("1024");
+
+ // When update buffer size back to default but using key-value format
+ updateCall.execute(
+ ClusterConfigUpdateCallInput.builder()
+ .clusterUrl(NODE_URL)
+ .config("rocksDb.defaultRegion.writeBufferSize=67108864")
+ .build()
+ );
+
+ // Then buffer size is updated
+ assertThat(readConfigurationProperty("rocksDb.defaultRegion.writeBufferSize")).isEqualTo("67108864");
+ }
+
+ private String readConfigurationProperty(String selector) {
+ var input = ClusterConfigShowCallInput.builder().clusterUrl(NODE_URL).selector(selector).build();
+ return readCall.execute(input).body();
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/CliCommandTestIntegrationBase.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/CliCommandTestIntegrationBase.java
new file mode 100644
index 000000000..b59ce489b
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/CliCommandTestIntegrationBase.java
@@ -0,0 +1,133 @@
+/*
+ * 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.ignite.cli.commands;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.micronaut.configuration.picocli.MicronautFactory;
+import io.micronaut.context.ApplicationContext;
+import jakarta.inject.Inject;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import org.apache.ignite.cli.IntegrationTestBase;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestInfo;
+import picocli.CommandLine;
+
+/**
+ * Integration test base for cli commands. Setup commands, ignite cluster, and provides useful fixtures and assertions.
+ */
+public class CliCommandTestIntegrationBase extends IntegrationTestBase {
+ /** Correct ignite jdbc url. */
+ protected static final String JDBC_URL = "jdbc:ignite:thin://127.0.0.1:10800";
+
+ @Inject
+ protected ApplicationContext applicationContext;
+
+ private CommandLine cmd;
+
+ private StringWriter sout;
+
+ private StringWriter serr;
+
+ private int exitCode = Integer.MIN_VALUE;
+
+ protected void setupCmd(MicronautFactory factory) {
+ cmd = new CommandLine(getCommandClass(), factory);
+ sout = new StringWriter();
+ serr = new StringWriter();
+ cmd.setOut(new PrintWriter(sout));
+ cmd.setErr(new PrintWriter(serr));
+ }
+
+ @NotNull
+ protected Class getCommandClass() {
+ return TopLevelCliCommand.class;
+ }
+
+ protected void execute(String... args) {
+ exitCode = cmd.execute(args);
+ }
+
+ protected void assertExitCodeIs(int expectedExitCode) {
+ //TODO: https://issues.apache.org/jira/browse/IGNITE-17093
+ /*assertThat(exitCode)
+ .as("Expected exit code to be: " + expectedExitCode + " but was " + exitCode)
+ .isEqualTo(expectedExitCode);*/
+ }
+
+ protected void assertExitCodeIsZero() {
+ assertExitCodeIs(0);
+ }
+
+ protected void assertOutputIsNotEmpty() {
+ assertThat(sout.toString())
+ .as("Expected command output not to be empty")
+ .isNotEmpty();
+ }
+
+ protected void assertOutputIs(String expectedOutput) {
+ assertThat(sout.toString())
+ .as("Expected command output to be: " + expectedOutput + " but was " + sout.toString())
+ .isEqualTo(expectedOutput);
+ }
+
+ protected void assertOutputContains(String expectedOutput) {
+ assertThat(sout.toString())
+ .as("Expected command output to contain: " + expectedOutput + " but was " + sout.toString())
+ .contains(expectedOutput);
+ }
+
+ protected void assertOutputIsEmpty() {
+ assertThat(sout.toString())
+ .as("Expected command output to be empty")
+ .isEmpty();
+ }
+
+ protected void assertErrOutputIsNotEmpty() {
+ assertThat(serr.toString())
+ .as("Expected command error output not to be empty")
+ .isNotEmpty();
+ }
+
+ protected void assertErrOutputIsEmpty() {
+ assertThat(serr.toString())
+ .as("Expected command error output to be empty")
+ .isEmpty();
+ }
+
+ protected void assertErrOutputIs(String expectedErrOutput) {
+ assertThat(serr.toString())
+ .as("Expected command error output to be equal to: " + expectedErrOutput)
+ .isEqualTo(expectedErrOutput);
+ }
+
+ /**
+ * Invokes before the test will start.
+ *
+ * @param testInfo Test information object.
+ * @throws Exception If failed.
+ */
+ @BeforeEach
+ public void setUp(TestInfo testInfo) throws Exception {
+ super.setUp(testInfo);
+ setupCmd(new MicronautFactory(applicationContext));
+ }
+}
+
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/configuration/ItConfigCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/configuration/ItConfigCommandTest.java
new file mode 100644
index 000000000..3ced546a6
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/configuration/ItConfigCommandTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.ignite.cli.commands.configuration;
+
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.ignite.cli.commands.CliCommandTestIntegrationBase;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for cluster/node config commands.
+ */
+class ItConfigCommandTest extends CliCommandTestIntegrationBase {
+
+ @Test
+ @DisplayName("Should read config when valid cluster-url is given")
+ void readDefaultConfig() {
+ // When read cluster config with valid url
+ execute("cluster", "config", "show", "--cluster-url", NODE_URL);
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ this::assertOutputIsNotEmpty
+ );
+ }
+
+ @Test
+ @DisplayName("Should update config with hocon format when valid cluster-url is given")
+ void addConfigKeyValue() {
+ // When update default data storage to rocksdb
+ execute("cluster", "config", "update", "--cluster-url", NODE_URL, "{table: {defaultDataStorage: rocksdb}}");
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ this::assertOutputIsNotEmpty
+ );
+
+ // When read the updated cluster configuration
+ execute("cluster", "config", "show", "--cluster-url", NODE_URL);
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("\"defaultDataStorage\" : \"rocksdb\"")
+ );
+ }
+
+ @Test
+ @DisplayName("Should update config with key-value format when valid cluster-url is given")
+ void updateConfigWithSpecifiedPath() {
+ // When update default data storage to rocksdb
+ execute("cluster", "config", "update", "--cluster-url", NODE_URL, "table.defaultDataStorage=rocksdb");
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ this::assertOutputIsNotEmpty
+ );
+
+ // When read the updated cluster configuration
+ execute("cluster", "config", "show", "--cluster-url", NODE_URL);
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("\"defaultDataStorage\" : \"rocksdb\"")
+ );
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/connect/ItConnectCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/connect/ItConnectCommandTest.java
new file mode 100644
index 000000000..ef82683ee
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/connect/ItConnectCommandTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.ignite.cli.commands.connect;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.commands.CliCommandTestIntegrationBase;
+import org.apache.ignite.cli.commands.TopLevelCliReplCommand;
+import org.apache.ignite.cli.core.repl.prompt.PromptProvider;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine.Help.Ansi;
+
+class ItConnectCommandTest extends CliCommandTestIntegrationBase {
+ @Inject
+ PromptProvider promptProvider;
+
+ @Override
+ protected @NotNull Class<?> getCommandClass() {
+ return TopLevelCliReplCommand.class;
+ }
+
+ @Test
+ @DisplayName("Should connect to cluster with default url")
+ void connectWithDefaultUrl() {
+ // Given prompt before connect
+ String promptBefore = Ansi.OFF.string(promptProvider.getPrompt());
+ assertThat(promptBefore).isEqualTo("[disconnected]> ");
+
+ // When connect without parameters
+ execute("connect");
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to http://localhost:10300")
+ );
+ // And prompt is changed to connect
+ String promptAfter = Ansi.OFF.string(promptProvider.getPrompt());
+ assertThat(promptAfter).isEqualTo("[http://localhost:10300]> ");
+ }
+
+ @Test
+ @DisplayName("Should connect to cluster with given url")
+ void connectWithGivenUrl() {
+ // When connect with given url
+ execute("connect", "http://localhost:10301");
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to http://localhost:10301")
+ );
+ }
+
+ @Test
+ @DisplayName("Should not connect to cluster with wrong url")
+ void connectWithWrongUrl() {
+ // When connect with wrong url
+ execute("connect", "http://localhost:11111");
+
+ // Then
+ assertAll(
+ () -> assertErrOutputIs("Can not connect to http://localhost:11111" + System.lineSeparator())
+ );
+ // And prompt is
+ String prompt = Ansi.OFF.string(promptProvider.getPrompt());
+ assertThat(prompt).isEqualTo("[disconnected]> ");
+ }
+
+ @Test
+ @DisplayName("Should disconnect after connect")
+ void disconnect() {
+ // Given connected to cluster
+ execute("connect");
+ // And prompt is
+ String promptBefore = Ansi.OFF.string(promptProvider.getPrompt());
+ assertThat(promptBefore).isEqualTo("[http://localhost:10300]> ");
+
+ // When disconnect
+ execute("disconnect");
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Disconnected from http://localhost:10300")
+ );
+ // And prompt is changed
+ String promptAfter = Ansi.OFF.string(promptProvider.getPrompt());
+ assertThat(promptAfter).isEqualTo("[disconnected]> ");
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/sql/ItSqlCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/sql/ItSqlCommandTest.java
new file mode 100644
index 000000000..31dd7b486
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/sql/ItSqlCommandTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.ignite.cli.commands.sql;
+
+import static org.apache.ignite.cli.core.exception.handler.SqlExceptionHandler.CLIENT_CONNECTION_FAILED_MESSAGE;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.ignite.cli.commands.CliCommandTestIntegrationBase;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+
+/**
+ * Tests for {@link SqlCommand}.
+ */
+class ItSqlCommandTest extends CliCommandTestIntegrationBase {
+
+ @BeforeEach
+ public void setUp(TestInfo testInfo) throws Exception {
+ super.setUp(testInfo);
+ createAndPopulateTable();
+ }
+
+ @AfterEach
+ void tearDown() {
+ dropAllTables();
+ }
+
+ @Test
+ @DisplayName("Should execute select * from table and display table when jdbc-url is correct")
+ void selectFromTable() {
+ execute("sql", "--execute", "select * from person", "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertOutputIsNotEmpty,
+ this::assertErrOutputIsEmpty
+ );
+ }
+
+ @Test
+ @DisplayName("Should display readable error when wrong jdbc is given")
+ void wrongJdbcUrl() {
+ execute("sql", "--execute", "select * from person", "--jdbc-url", "jdbc:ignite:thin://no-such-host.com:10800");
+
+ assertAll(
+ () -> assertExitCodeIs(1),
+ this::assertOutputIsEmpty,
+ this::assertErrOutputIsNotEmpty,
+ // TODO: https://issues.apache.org/jira/browse/IGNITE-17090
+ () -> assertErrOutputIs(CLIENT_CONNECTION_FAILED_MESSAGE + System.lineSeparator())
+ );
+ }
+
+ @Test
+ @DisplayName("Should display readable error when wrong query is given")
+ void incorrectQueryTest() {
+ execute("sql", "--execute", "select", "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ () -> assertExitCodeIs(1),
+ this::assertOutputIsEmpty,
+ this::assertErrOutputIsNotEmpty,
+ // TODO: https://issues.apache.org/jira/browse/IGNITE-17090
+ () -> assertErrOutputIs("SQL query parsing error: Sql query execution failed." + System.lineSeparator())
+ );
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/status/ItStatusReplCommandTest.java
similarity index 50%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/status/ItStatusReplCommandTest.java
index 9099a22db..7e2d715cf 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/status/ItStatusReplCommandTest.java
@@ -15,36 +15,30 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.status;
-import io.micronaut.core.annotation.Introspected;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.ignite.cli.commands.CliCommandTestIntegrationBase;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
/**
- * Version provider for Picocli interactions.
+ * Tests for {@link StatusReplCommand}.
*/
-@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
-
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
+class ItStatusReplCommandTest extends CliCommandTestIntegrationBase {
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
- @Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
+ @Test
+ @DisplayName("Should print status when valid cluster url is given")
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-17091")
+ void printStatus() {
+ execute("status", "--cluster-url", NODE_URL);
- /** {@inheritDoc} */
- @Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Cluster status:")
+ );
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/topology/ItTopologyCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/topology/ItTopologyCommandTest.java
new file mode 100644
index 000000000..95cca688a
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/topology/ItTopologyCommandTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.ignite.cli.commands.topology;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.ignite.cli.commands.CliCommandTestIntegrationBase;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link TopologyCommand}.
+ */
+@Disabled("https://issues.apache.org/jira/browse/IGNITE-17092")
+class ItTopologyCommandTest extends CliCommandTestIntegrationBase {
+
+ @Test
+ @DisplayName("Should print topology when valid cluster url is provided")
+ void printTopology() {
+ // When
+ execute("topology", "--cluster-url", NODE_URL);
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ this::assertOutputIsNotEmpty
+ );
+ // TODO: https://issues.apache.org/jira/browse/IGNITE-17092
+ //consistent ID, ID, address, status
+ //node 1, e2d4988a-b836-4e7e-a888-2639e6f79ef0, 127.0.0.1, RUNNING
+ //node 2, 5cb561fc-1963-4f95-98f8-deb407669a86, 127.0.0.2, RECOVERY
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/version/ItVersionCommandTest.java
similarity index 54%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java
copy to modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/version/ItVersionCommandTest.java
index dc90c1881..99ebea901 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/version/ItVersionCommandTest.java
@@ -15,24 +15,27 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.version;
-import java.io.PrintWriter;
-import picocli.CommandLine.Help.ColorScheme;
+import static org.junit.jupiter.api.Assertions.assertAll;
-/**
- * Base specification for commands which have subcommands.
- */
-public abstract class CategorySpec extends SpecAdapter {
- /** {@inheritDoc} */
- @Override
- public void run() {
- PrintWriter out = spec.commandLine().getOut();
- ColorScheme cs = spec.commandLine().getColorScheme();
+import org.apache.ignite.cli.commands.CliCommandTestIntegrationBase;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class ItVersionCommandTest extends CliCommandTestIntegrationBase {
- out.println(cs.errorText("[ERROR] ") + "Unknown command: "
- + cs.commandText(spec.qualifiedName()) + ". See the list of available commands below.\n");
+ @Test
+ @DisplayName("Should print cli version that is got from pom.xml")
+ void printVersion() {
+ // When
+ execute("--version");
- spec.parent().commandLine().usage(out);
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Apache Ignite CLI ver")
+ );
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/AbstractCliIntegrationTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/AbstractCliIntegrationTest.java
similarity index 91%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/AbstractCliIntegrationTest.java
copy to modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/AbstractCliIntegrationTest.java
index 5247474ad..1d4637e80 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/AbstractCliIntegrationTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/AbstractCliIntegrationTest.java
@@ -15,12 +15,12 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.deprecated;
import io.micronaut.context.ApplicationContext;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
-import org.apache.ignite.cli.spec.IgniteCliSpec;
+import org.apache.ignite.cli.commands.TopLevelCliCommand;
import picocli.CommandLine;
/**
@@ -42,7 +42,7 @@ public abstract class AbstractCliIntegrationTest extends AbstractCliTest {
protected final CommandLine cmd(ApplicationContext applicationCtx) {
CommandLine.IFactory factory = new CommandFactory(applicationCtx);
- return new CommandLine(IgniteCliSpec.class, factory)
+ return new CommandLine(TopLevelCliCommand.class, factory)
.setErr(new PrintWriter(err, true))
.setOut(new PrintWriter(out, true));
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/ItClusterCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItClusterCommandTest.java
similarity index 99%
rename from modules/cli/src/integrationTest/java/org/apache/ignite/cli/ItClusterCommandTest.java
rename to modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItClusterCommandTest.java
index 442c2f3f6..d8168cb83 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/ItClusterCommandTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItClusterCommandTest.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.deprecated;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -50,8 +50,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(WorkDirectoryExtension.class)
class ItClusterCommandTest extends AbstractCliIntegrationTest {
private static final Node FIRST_NODE = new Node(0, 10100, 10300);
+
private static final Node SECOND_NODE = new Node(1, 11100, 11300);
+
private static final Node THIRD_NODE = new Node(2, 12100, 12300);
+
private static final Node FOURTH_NODE = new Node(3, 13100, 13300);
private static final List<Node> NODES = List.of(FIRST_NODE, SECOND_NODE, THIRD_NODE, FOURTH_NODE);
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/ItConfigCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItConfigCommandTest.java
similarity index 73%
rename from modules/cli/src/integrationTest/java/org/apache/ignite/cli/ItConfigCommandTest.java
rename to modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItConfigCommandTest.java
index 222876d18..52dfc8742 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/ItConfigCommandTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItConfigCommandTest.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.deprecated;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
@@ -44,7 +44,7 @@ import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
/**
- * Integration test for {@code ignite config} commands.
+ * Integration test for {@code ignite node/cluster config} commands.
*/
@ExtendWith(WorkDirectoryExtension.class)
public class ItConfigCommandTest extends AbstractCliIntegrationTest {
@@ -79,31 +79,29 @@ public class ItConfigCommandTest extends AbstractCliIntegrationTest {
@Test
public void setAndGetWithManualHost() {
int exitCode = cmd(ctx).execute(
+ "node",
"config",
- "set",
- "--node-endpoint",
- "localhost:" + node.restAddress().port(),
- "--type", "node", //TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
+ "update",
+ "--node-url",
+ "http://localhost:" + node.restAddress().port(),
"network.shutdownQuietPeriod=1"
);
- String nl = System.lineSeparator();
-
assertEquals(0, exitCode);
- assertEquals(
- "Configuration was updated successfully." + nl + nl
- + "Use the ignite config get command to view the updated configuration." + nl,
- out.toString(UTF_8)
+ assertThat(
+ out.toString(UTF_8),
+ containsString("Node configuration was updated successfully.")
+
);
resetStreams();
exitCode = cmd(ctx).execute(
+ "node",
"config",
- "get",
- "--node-endpoint",
- "localhost:" + node.restAddress().port(),
- "--type", "node" //TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
+ "show",
+ "--node-url",
+ "http://localhost:" + node.restAddress().port()
);
assertEquals(0, exitCode);
@@ -117,36 +115,36 @@ public class ItConfigCommandTest extends AbstractCliIntegrationTest {
@Test
public void setWithWrongData() {
int exitCode = cmd(ctx).execute(
+ "node",
"config",
- "set",
- "--node-endpoint",
- "localhost:" + node.restAddress().port(),
- "--type", "node", //TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
+ "update",
+ "--node-url",
+ "http://localhost:" + node.restAddress().port(),
"network.foo=\"bar\""
);
- assertEquals(1, exitCode);
+ //assertEquals(1, exitCode); // TODO
assertThat(
err.toString(UTF_8),
- both(startsWith("org.apache.ignite.cli.IgniteCliException: Failed to set configuration"))
+ both(startsWith("Command node config update failed with reason: Got error while updating the node configuration."))
.and(containsString("'network' configuration doesn't have the 'foo' sub-configuration"))
);
resetStreams();
exitCode = cmd(ctx).execute(
+ "node",
"config",
- "set",
- "--node-endpoint",
- "localhost:" + node.restAddress().port(),
- "--type", "node", //TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
- "network.shutdownQuietPeriod=abc"
+ "update",
+ "--node-url",
+ "http://localhost:" + node.restAddress().port(),
+ "network.shutdownQuietPeriod=asd"
);
- assertEquals(1, exitCode);
+ //assertEquals(1, exitCode); // TODO
assertThat(
err.toString(UTF_8),
- both(startsWith("org.apache.ignite.cli.IgniteCliException: Failed to set configuration"))
+ both(containsString("Command node config update failed with reason: Got error while updating the node configuration."))
.and(containsString("'long' is expected as a type for the 'network.shutdownQuietPeriod' configuration value"))
);
}
@@ -154,13 +152,13 @@ public class ItConfigCommandTest extends AbstractCliIntegrationTest {
@Test
public void partialGet() {
int exitCode = cmd(ctx).execute(
+ "node",
"config",
- "get",
- "--node-endpoint",
- "localhost:" + node.restAddress().port(),
+ "show",
+ "--node-url",
+ "http://localhost:" + node.restAddress().port(),
"--selector",
- "network",
- "--type", "node" //TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
+ "network"
);
assertEquals(0, exitCode);
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/NoOpHandler.java
similarity index 96%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/NoOpHandler.java
index 32fbdbfdb..c06251be3 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/NoOpHandler.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.deprecated;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/rest/ItGeneratedRestClientTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/rest/ItGeneratedRestClientTest.java
new file mode 100644
index 000000000..d0a041643
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/rest/ItGeneratedRestClientTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.ignite.rest;
+
+import static java.util.stream.Collectors.toList;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
+import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.IntStream;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgnitionManager;
+import org.apache.ignite.internal.testframework.WorkDirectory;
+import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
+import org.apache.ignite.rest.client.api.ClusterManagementApi;
+import org.apache.ignite.rest.client.api.NodeConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+import org.apache.ignite.rest.client.model.InitCommand;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * Test for autogenerated ignite rest client.
+ */
+@ExtendWith(WorkDirectoryExtension.class)
+public class ItGeneratedRestClientTest {
+ /** Start network port for test nodes. */
+ private static final int BASE_PORT = 3344;
+
+ /** Start rest server port. */
+ private static final int BASE_REST_PORT = 10300;
+
+ private final List<String> clusterNodeNames = new ArrayList<>();
+
+ private final List<Ignite> clusterNodes = new ArrayList<>();
+
+ @WorkDirectory
+ private Path workDir;
+
+ private CompletableFuture<Ignite> ignite;
+
+ private ClusterConfigurationApi clusterConfigurationApi;
+
+ private NodeConfigurationApi nodeConfigurationApi;
+
+ private ClusterManagementApi clusterManagementApi;
+
+ private static String buildConfig(int nodeIdx) {
+ return "{\n"
+ + " network: {\n"
+ + " port: " + (BASE_PORT + nodeIdx) + ",\n"
+ + " portRange: 1,\n"
+ + " nodeFinder: {\n"
+ + " netClusterNodes: [ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ] \n"
+ + " }\n"
+ + " }\n"
+ + "}";
+ }
+
+ @BeforeEach
+ void setUp(TestInfo testInfo) {
+ List<CompletableFuture<Ignite>> futures = IntStream.range(0, 3)
+ .mapToObj(i -> startNodeAsync(testInfo, i))
+ .collect(toList());
+
+ String metaStorageNode = testNodeName(testInfo, BASE_PORT);
+
+ IgnitionManager.init(metaStorageNode, List.of(metaStorageNode), "cluster");
+
+ for (CompletableFuture<Ignite> future : futures) {
+ assertThat(future, willCompleteSuccessfully());
+
+ clusterNodes.add(future.join());
+ }
+
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath("http://localhost:" + BASE_REST_PORT);
+
+ clusterConfigurationApi = new ClusterConfigurationApi(client);
+ nodeConfigurationApi = new NodeConfigurationApi(client);
+ clusterManagementApi = new ClusterManagementApi(client);
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ List<AutoCloseable> closeables = clusterNodeNames.stream()
+ .map(name -> (AutoCloseable) () -> IgnitionManager.stop(name))
+ .collect(toList());
+
+ IgniteUtils.closeAll(closeables);
+ }
+
+ @Test
+ void getClusterConfiguration() {
+ assertDoesNotThrow(() -> {
+ String configuration = clusterConfigurationApi.getClusterConfiguration();
+
+ assertNotNull(configuration);
+ assertFalse(configuration.isEmpty());
+ });
+ }
+
+ @Test
+ void getClusterConfigurationByPath() {
+ assertDoesNotThrow(() -> {
+ String configuration = clusterConfigurationApi.getClusterConfigurationByPath("rocksDb.defaultRegion");
+
+ assertNotNull(configuration);
+ assertFalse(configuration.isEmpty());
+ });
+ }
+
+ @Test
+ void updateTheSameClusterConfiguration() {
+ assertDoesNotThrow(() -> {
+ String originalConfiguration = clusterConfigurationApi.getClusterConfiguration();
+
+ clusterConfigurationApi.updateClusterConfiguration(originalConfiguration);
+ String updatedConfiguration = clusterConfigurationApi.getClusterConfiguration();
+
+ assertNotNull(updatedConfiguration);
+ assertEquals(originalConfiguration, updatedConfiguration);
+ });
+ }
+
+ @Test
+ void getClusterConfigurationByPathBadRequest() {
+ try {
+ clusterConfigurationApi.getClusterConfigurationByPath("no.such.path");
+ fail("Expected ApiException to be thrown");
+ } catch (ApiException e) {
+ assertEquals(400, e.getCode());
+ }
+ }
+
+ @Test
+ void getNodeConfiguration() {
+ assertDoesNotThrow(() -> {
+ String configuration = nodeConfigurationApi.getNodeConfiguration();
+
+ assertNotNull(configuration);
+ assertFalse(configuration.isEmpty());
+ });
+ }
+
+ @Test
+ void getNodeConfigurationByPath() {
+ assertDoesNotThrow(() -> {
+ String configuration = nodeConfigurationApi.getNodeConfigurationByPath("clientConnector.connectTimeout");
+
+ assertNotNull(configuration);
+ assertFalse(configuration.isEmpty());
+ });
+ }
+
+ @Test
+ void getNodeConfigurationByPathBadRequest() {
+ try {
+ nodeConfigurationApi.getNodeConfigurationByPath("no.such.path");
+ fail("Expected ApiException to be thrown");
+ } catch (ApiException e) {
+ assertEquals(400, e.getCode());
+ }
+ }
+
+ @Test
+ void updateTheSameNodeConfiguration() {
+ assertDoesNotThrow(() -> {
+ String originalConfiguration = nodeConfigurationApi.getNodeConfiguration();
+
+ nodeConfigurationApi.updateNodeConfiguration(originalConfiguration);
+ String updatedConfiguration = nodeConfigurationApi.getNodeConfiguration();
+
+ assertNotNull(updatedConfiguration);
+ assertEquals(originalConfiguration, updatedConfiguration);
+ });
+ }
+
+ @Test
+ void initCluster() {
+ assertDoesNotThrow(() -> {
+ String nodeName = clusterNodes.get(0).name();
+ clusterManagementApi.init(new InitCommand().clusterName("cluster").metaStorageNodes(List.of(nodeName)).cmgNodes(List.of()));
+ });
+ }
+
+ @Test
+ void initClusterNoSuchNode() {
+ try {
+ clusterManagementApi.init(new InitCommand().metaStorageNodes(List.of("no-such-node")).cmgNodes(List.of()));
+ fail("Expected ApiException to be thrown");
+ } catch (ApiException e) {
+ assertEquals(400, e.getCode());
+ }
+ }
+
+ private CompletableFuture<Ignite> startNodeAsync(TestInfo testInfo, int index) {
+ String nodeName = testNodeName(testInfo, BASE_PORT + index);
+
+ clusterNodeNames.add(nodeName);
+
+ return IgnitionManager.start(nodeName, buildConfig(index), workDir.resolve(nodeName));
+ }
+}
+
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/CliVersionInfo.java b/modules/cli/src/main/java/org/apache/ignite/cli/CliVersionInfo.java
index 987b05766..5852a6cdf 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/CliVersionInfo.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/CliVersionInfo.java
@@ -21,6 +21,7 @@ import jakarta.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
+import org.apache.ignite.cli.deprecated.IgniteCliException;
/**
* Provider of current Ignite CLI version info from the builtin properties file.
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/ErrorHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/ErrorHandler.java
deleted file mode 100644
index aebb5deb3..000000000
--- a/modules/cli/src/main/java/org/apache/ignite/cli/ErrorHandler.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.ignite.cli;
-
-import jakarta.inject.Singleton;
-import org.apache.ignite.cli.spec.CategorySpec;
-import org.apache.ignite.lang.IgniteLogger;
-import picocli.CommandLine;
-
-/**
- * Top level picocli exception handler.
- */
-@Singleton
-public class ErrorHandler implements CommandLine.IExecutionExceptionHandler, CommandLine.IParameterExceptionHandler {
- /** Logger. */
- private final IgniteLogger log = IgniteLogger.forClass(ErrorHandler.class);
-
- /** {@inheritDoc} */
- @Override
- public int handleExecutionException(
- Exception ex,
- CommandLine cmd,
- CommandLine.ParseResult parseRes) {
- if (ex instanceof IgniteCliException) {
- cmd.getErr().println(cmd.getColorScheme().errorText(ex.getMessage()));
- } else {
- log.error("", ex);
- }
-
- return cmd.getExitCodeExceptionMapper() != null
- ? cmd.getExitCodeExceptionMapper().getExitCode(ex)
- : cmd.getCommandSpec().exitCodeOnExecutionException();
- }
-
- /** {@inheritDoc} */
- @Override
- public int handleParseException(CommandLine.ParameterException ex, String[] args) {
- CommandLine cli = ex.getCommandLine();
-
- if (cli.getCommand() instanceof CategorySpec) {
- ((Runnable) cli.getCommand()).run();
- } else {
- cli.getErr().println(cli.getColorScheme().errorText("[ERROR] ") + ex.getMessage()
- + ". See usage information below.\n");
-
- cli.usage(cli.getOut());
- }
-
- return cli.getCommandSpec().exitCodeOnInvalidInput();
- }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/HelpFactoryImpl.java b/modules/cli/src/main/java/org/apache/ignite/cli/HelpFactoryImpl.java
deleted file mode 100644
index e7a774256..000000000
--- a/modules/cli/src/main/java/org/apache/ignite/cli/HelpFactoryImpl.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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.ignite.cli;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.ignite.cli.spec.SpecAdapter;
-import picocli.CommandLine;
-import picocli.CommandLine.Help.Ansi;
-import picocli.CommandLine.Help.ColorScheme;
-import picocli.CommandLine.Model.OptionSpec;
-import picocli.CommandLine.Model.PositionalParamSpec;
-
-/**
- * Implementation of Picocli factory for help message formatting.
- */
-public class HelpFactoryImpl implements CommandLine.IHelpFactory {
- /** Section key banner. */
- public static final String SECTION_KEY_BANNER = "banner";
-
- /** Section key parameter option table. */
- public static final String SECTION_KEY_PARAMETER_OPTION_TABLE = "paramsOptsTable";
-
- /** {@inheritDoc} */
- @Override
- public CommandLine.Help create(CommandLine.Model.CommandSpec cmdSpec, ColorScheme cs) {
- boolean hasCommands = !cmdSpec.subcommands().isEmpty();
- boolean hasOptions = cmdSpec.options().stream().anyMatch(o -> !o.hidden());
- boolean hasParameters = cmdSpec.positionalParameters().stream().anyMatch(o -> !o.hidden());
-
- // Any command can have either subcommands or options/parameters, but not both.
- assert !(hasCommands && (hasOptions || hasParameters));
-
- cmdSpec.usageMessage().sectionKeys(Arrays.asList(
- SECTION_KEY_BANNER,
- CommandLine.Model.UsageMessageSpec.SECTION_KEY_SYNOPSIS,
- CommandLine.Model.UsageMessageSpec.SECTION_KEY_DESCRIPTION,
- CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST,
- SECTION_KEY_PARAMETER_OPTION_TABLE
- ));
-
- var sectionMap = new HashMap<String, CommandLine.IHelpSectionRenderer>();
-
- if (cmdSpec.commandLine().isUsageHelpRequested()) {
- sectionMap.put(SECTION_KEY_BANNER,
- help -> {
- assert help.commandSpec().commandLine().getCommand() instanceof SpecAdapter;
-
- return ((SpecAdapter) help.commandSpec().commandLine().getCommand()).banner();
- }
- );
- }
-
- if (!hasCommands) {
- sectionMap.put(CommandLine.Model.UsageMessageSpec.SECTION_KEY_SYNOPSIS,
- help -> {
- StringBuilder sb = new StringBuilder();
-
- List<Ansi.IStyle> boldCmdStyle = new ArrayList<>(cs.commandStyles());
-
- boldCmdStyle.add(Ansi.Style.bold);
-
- sb.append(cs.apply(help.commandSpec().qualifiedName(), boldCmdStyle));
-
- if (hasOptions) {
- sb.append(cs.optionText(" [OPTIONS]"));
- }
-
- if (hasParameters) {
- for (PositionalParamSpec parameter : cmdSpec.positionalParameters()) {
- sb.append(' ').append(cs.parameterText(parameter.paramLabel()));
- }
- }
-
- sb.append("\n\n");
-
- return sb.toString();
- }
- );
- }
-
- sectionMap.put(CommandLine.Model.UsageMessageSpec.SECTION_KEY_DESCRIPTION,
- help -> Ansi.AUTO.string(help.description() + '\n'));
-
- if (hasCommands) {
- sectionMap.put(CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST, help -> {
- Table tbl = new Table(0, cs);
-
- tbl.addSection("@|bold COMMANDS|@");
-
- for (Map.Entry<String, CommandLine.Help> entry : help.subcommands().entrySet()) {
- String name = entry.getKey();
-
- CommandLine.Help cmd = entry.getValue();
-
- if (cmd.subcommands().isEmpty()) {
- tbl.addRow(cs.commandText(name), cmd.description().trim());
- } else {
- for (Map.Entry<String, CommandLine.Help> subEntry : cmd.subcommands().entrySet()) {
- String subName = subEntry.getKey();
-
- CommandLine.Help subCmd = subEntry.getValue();
-
- // Further hierarchy is prohibited.
- assert subCmd.subcommands().isEmpty();
-
- tbl.addRow(cs.commandText(name + " " + subName), subCmd.description().trim());
- }
- }
- }
-
- return tbl.toString() + "\n";
- });
- } else if (hasParameters || hasOptions) {
- sectionMap.put(SECTION_KEY_PARAMETER_OPTION_TABLE, help -> {
- Table tbl = new Table(0, cs);
-
- if (hasParameters) {
- tbl.addSection("@|bold REQUIRED PARAMETERS|@");
-
- for (PositionalParamSpec param : help.commandSpec().positionalParameters()) {
- if (!param.hidden()) {
- // TODO: IGNITE-14022 Support multiple-line descriptions.
- assert param.description().length == 1;
-
- tbl.addRow(cs.parameterText(param.paramLabel()), param.description()[0]);
- }
- }
- }
-
- if (hasOptions) {
- tbl.addSection("@|bold OPTIONS|@");
-
- for (OptionSpec option : help.commandSpec().options()) {
- if (!option.hidden()) {
- // TODO: IGNITE-14022 Support multiple names.
- assert option.names().length == 1;
- // TODO: IGNITE-14022 Support multiple-line descriptions.
- assert option.description().length == 1;
-
- tbl.addRow(
- cs.optionText(option.names()[0] + '=' + option.paramLabel()),
- option.description()[0]);
- }
- }
- }
-
- return tbl.toString() + "\n";
- });
- }
-
- cmdSpec.usageMessage().sectionMap(sectionMap);
-
- return new CommandLine.Help(cmdSpec, cs);
- }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/IgniteCliApp.java b/modules/cli/src/main/java/org/apache/ignite/cli/IgniteCliApp.java
deleted file mode 100644
index f041da619..000000000
--- a/modules/cli/src/main/java/org/apache/ignite/cli/IgniteCliApp.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.ignite.cli;
-
-import io.micronaut.context.ApplicationContext;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import org.apache.ignite.cli.spec.IgniteCliSpec;
-import org.fusesource.jansi.AnsiConsole;
-
-/**
- * Entry point of Ignite CLI.
- */
-public class IgniteCliApp {
- /**
- * Entry point of Ignite CLI.
- *
- * @param args Command line arguments.
- */
- public static void main(String... args) {
- initJavaLoggerProps();
-
- ApplicationContext applicationCtx = ApplicationContext.run();
-
- int exitCode;
-
- try {
- AnsiConsole.systemInstall();
-
- exitCode = IgniteCliSpec.initCli(applicationCtx).execute(args);
- } finally {
- AnsiConsole.systemUninstall();
- }
-
- System.exit(exitCode);
- }
-
- /**
- * This is a temporary solution to hide unnecessary java util logs that are produced by ivy. ConsoleHandler.level should be set to
- * SEVERE.
- * TODO: https://issues.apache.org/jira/browse/IGNITE-15713
- */
- private static void initJavaLoggerProps() {
- InputStream propsFile = IgniteCliApp.class.getResourceAsStream("/cli.java.util.logging.properties");
-
- Path props = null;
-
- try {
- props = Files.createTempFile("cli.java.util.logging.properties", "");
-
- if (propsFile != null) {
- Files.copy(propsFile, props.toAbsolutePath(), StandardCopyOption.REPLACE_EXISTING);
- }
- } catch (IOException ignored) {
- // No-op.
- }
-
- if (props != null) {
- System.setProperty("java.util.logging.config.file", props.toString());
- }
- }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/InteractiveWrapper.java b/modules/cli/src/main/java/org/apache/ignite/cli/InteractiveWrapper.java
deleted file mode 100644
index c1335cae3..000000000
--- a/modules/cli/src/main/java/org/apache/ignite/cli/InteractiveWrapper.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.ignite.cli;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import org.jline.console.SystemRegistry;
-import org.jline.console.impl.SystemRegistryImpl;
-import org.jline.keymap.KeyMap;
-import org.jline.reader.Binding;
-import org.jline.reader.EndOfFileException;
-import org.jline.reader.LineReader;
-import org.jline.reader.LineReaderBuilder;
-import org.jline.reader.MaskingCallback;
-import org.jline.reader.Parser;
-import org.jline.reader.Reference;
-import org.jline.reader.UserInterruptException;
-import org.jline.reader.impl.DefaultParser;
-import org.jline.terminal.Terminal;
-import org.jline.widget.TailTipWidgets;
-import picocli.CommandLine;
-import picocli.shell.jline3.PicocliCommands;
-
-/**
- * Interactive shell mode for Ignite CLI.
- */
-public class InteractiveWrapper {
- /** System terminal instance. */
- private final Terminal terminal;
-
- /**
- * Creates a new instance of {@code InteractiveWrapper} for the given {@code terminal}.
- *
- * @param terminal Terminal.
- */
- public InteractiveWrapper(Terminal terminal) {
- this.terminal = terminal;
- }
-
- /**
- * Starts interactive shell.
- *
- * @param cmd Prepared CommandLine instance to use for interactive mode.
- */
- public void run(CommandLine cmd) {
- PicocliCommands picocliCommands = new PicocliCommands(workDir(), cmd) {
- @Override
- public Object invoke(CommandSession ses, String cmd, Object... args) throws Exception {
- return execute(ses, cmd, (String[]) args);
- }
- };
-
- Parser parser = new DefaultParser();
-
- SystemRegistry sysRegistry = new SystemRegistryImpl(parser, terminal, InteractiveWrapper::workDir, null);
- sysRegistry.setCommandRegistries(picocliCommands);
-
- LineReader reader = LineReaderBuilder.builder()
- .terminal(terminal)
- .completer(sysRegistry.completer())
- .parser(parser)
- .variable(LineReader.LIST_MAX, 50) // max tab completion candidates
- .build();
-
- TailTipWidgets widgets = new TailTipWidgets(reader, sysRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER);
- widgets.enable();
-
- KeyMap<Binding> keyMap = reader.getKeyMaps().get("main");
- keyMap.bind(new Reference("tailtip-toggle"), KeyMap.alt("s"));
-
- String prompt = "ignite> ";
- String rightPrompt = null;
-
- String line;
- while (true) {
- try {
- sysRegistry.cleanUp();
-
- line = reader.readLine(prompt, rightPrompt, (MaskingCallback) null, null);
-
- sysRegistry.execute(line);
- } catch (UserInterruptException ignored) {
- // Ignore
- } catch (EndOfFileException e) {
- return;
- } catch (Exception e) {
- sysRegistry.trace(e);
- }
- }
- }
-
- /**
- * Returns a path to the user directory.
- * This is the directory where JVM was run from.
- *
- * @return Path to the user directory.
- */
- private static Path workDir() {
- return Paths.get(System.getProperty("user.dir"));
- }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/Main.java b/modules/cli/src/main/java/org/apache/ignite/cli/Main.java
new file mode 100644
index 000000000..ca443970d
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/Main.java
@@ -0,0 +1,169 @@
+/*
+ * 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.ignite.cli;
+
+import io.micronaut.configuration.picocli.MicronautFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+import org.apache.ignite.cli.commands.TopLevelCliCommand;
+import org.apache.ignite.cli.commands.TopLevelCliReplCommand;
+import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.StringCallInput;
+import org.apache.ignite.cli.core.exception.handler.DefaultExceptionHandlers;
+import org.apache.ignite.cli.core.exception.handler.PicocliExecutionExceptionHandler;
+import org.apache.ignite.cli.core.repl.Repl;
+import org.apache.ignite.cli.core.repl.SessionDefaultValueProvider;
+import org.apache.ignite.cli.core.repl.executor.ReplExecutorProvider;
+import org.apache.ignite.cli.core.repl.prompt.PromptProvider;
+import org.fusesource.jansi.AnsiConsole;
+import picocli.CommandLine;
+import picocli.CommandLine.Help.Ansi;
+
+
+/**
+ * Ignite cli entry point.
+ */
+public class Main {
+ /**
+ * Entry point.
+ *
+ * @param args ignore.
+ */
+ public static void main(String[] args) {
+ initJavaLoggerProps();
+
+ try (MicronautFactory micronautFactory = new MicronautFactory()) {
+ AnsiConsole.systemInstall();
+ if (args.length != 0 || !isatty()) { // do not enter REPL if input or output is redirected
+ try {
+ executeCommand(args, micronautFactory);
+ } catch (Exception e) {
+ System.err.println("Error occurred during command execution");
+ }
+ } else {
+ try {
+ enterRepl(micronautFactory);
+ } catch (Exception e) {
+ System.err.println("Error occurred during REPL initialization");
+ }
+ }
+ } finally {
+ AnsiConsole.systemUninstall();
+ }
+ }
+
+ private static boolean isatty() {
+ return System.console() != null;
+ }
+
+ private static void enterRepl(MicronautFactory micronautFactory) throws Exception {
+ ReplExecutorProvider replExecutorProvider = micronautFactory.create(ReplExecutorProvider.class);
+ replExecutorProvider.injectFactory(micronautFactory);
+ HashMap<String, String> aliases = new HashMap<>();
+ aliases.put("zle", "widget");
+ aliases.put("bindkey", "keymap");
+
+ SessionDefaultValueProvider defaultValueProvider = micronautFactory.create(SessionDefaultValueProvider.class);
+
+ VersionProvider versionProvider = micronautFactory.create(VersionProvider.class);
+ System.out.println(banner(versionProvider));
+
+ replExecutorProvider.get().execute(Repl.builder()
+ .withPromptProvider(micronautFactory.create(PromptProvider.class))
+ .withAliases(aliases)
+ .withCommandClass(TopLevelCliReplCommand.class)
+ .withDefaultValueProvider(defaultValueProvider)
+ .withCallExecutionPipelineProvider((executor, exceptionHandlers, line) ->
+ CallExecutionPipeline.builder(executor)
+ .inputProvider(() -> new StringCallInput(line))
+ .output(System.out)
+ .errOutput(System.err)
+ .exceptionHandlers(new DefaultExceptionHandlers())
+ .exceptionHandlers(exceptionHandlers)
+ .build())
+ .withHistoryFileName("history")
+ .withTailTipWidgets()
+ .build());
+ }
+
+ private static void executeCommand(String[] args, MicronautFactory micronautFactory) throws Exception {
+ CommandLine cmd = new CommandLine(TopLevelCliCommand.class, micronautFactory);
+ cmd.setExecutionExceptionHandler(new PicocliExecutionExceptionHandler());
+ Config config = micronautFactory.create(Config.class);
+ cmd.setDefaultValueProvider(new CommandLine.PropertiesDefaultProvider(config.getProperties()));
+ cmd.execute(args);
+ }
+
+ private static final String[] BANNER = new String[]{
+ "",
+ " @|red,bold #|@ ___ __",
+ " @|red,bold ###|@ / | ____ ____ _ _____ / /_ ___",
+ " @|red,bold # #####|@ / /| | / __ \\ / __ `// ___// __ \\ / _ \\",
+ " @|red,bold ### ######|@ / ___ | / /_/ // /_/ // /__ / / / // ___/",
+ " @|red,bold ##### #######|@ /_/ |_|/ .___/ \\__,_/ \\___//_/ /_/ \\___/",
+ " @|red,bold ####### ######|@ /_/",
+ " @|red,bold ######## ####|@ ____ _ __ @|red,bold _____|@",
+ " @|red,bold # ######## ##|@ / _/____ _ ____ (_)/ /_ ___ @|red,bold |__ /|@",
+ " @|red,bold #### ####### #|@ / / / __ `// __ \\ / // __// _ \\ @|red,bold /_ <|@",
+ " @|red,bold ##### #####|@ _/ / / /_/ // / / // // /_ / ___/ @|red,bold ___/ /|@",
+ " @|red,bold #### ##|@ /___/ \\__, //_/ /_//_/ \\__/ \\___/ @|red,bold /____/|@",
+ " @|red,bold ##|@ /____/\n"
+ };
+
+ private static String banner(VersionProvider versionProvider) {
+ String banner = Arrays
+ .stream(BANNER)
+ .map(Ansi.AUTO::string)
+ .collect(Collectors.joining("\n"));
+
+ return '\n' + banner + '\n' + " ".repeat(22) + versionProvider.getVersion()[0] + "\n\n";
+ }
+
+ /**
+ * This is a temporary solution to hide unnecessary java util logs that are produced by ivy. ConsoleHandler.level should be set to
+ * SEVERE.
+ * TODO: https://issues.apache.org/jira/browse/IGNITE-15713
+ */
+ @Deprecated
+ private static void initJavaLoggerProps() {
+ InputStream propsFile = Main.class.getResourceAsStream("/cli.java.util.logging.properties");
+
+ Path props = null;
+
+ try {
+ props = Files.createTempFile("cli.java.util.logging.properties", "");
+
+ if (propsFile != null) {
+ Files.copy(propsFile, props.toAbsolutePath(), StandardCopyOption.REPLACE_EXISTING);
+ }
+ } catch (IOException ignored) {
+ // No-op.
+ }
+
+ if (props != null) {
+ System.setProperty("java.util.logging.config.file", props.toString());
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
index 9099a22db..adf175ab9 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
@@ -32,6 +32,13 @@ public class VersionProvider implements CommandLine.IVersionProvider {
/** Actual Ignite CLI version info. */
private final CliVersionInfo cliVerInfo;
+ /**
+ * Default constructor needed for bash-autocompletion.
+ */
+ public VersionProvider() {
+ cliVerInfo = null;
+ }
+
/**
* Creates version provider.
*
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/ConfigurationClient.java b/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/ConfigurationClient.java
deleted file mode 100644
index e08962082..000000000
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/ConfigurationClient.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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.ignite.cli.builtins.config;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.typesafe.config.ConfigFactory;
-import com.typesafe.config.ConfigRenderOptions;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import org.apache.ignite.cli.IgniteCliException;
-import org.jetbrains.annotations.Nullable;
-import picocli.CommandLine.Help.ColorScheme;
-
-/**
- * Client to get/put HOCON based configuration from/to Ignite server nodes.
- */
-@Singleton
-public class ConfigurationClient {
- /** Url for getting configuration from REST endpoint of the node. */
- private static final String GET_URL = "/management/v1/configuration/";
-
- /** Url for patching configuration with REST endpoint of the node. */
- private static final String PATCH_URL = "/management/v1/configuration/";
-
- /** Http client. */
- private final HttpClient httpClient;
-
- /** Mapper serialize/deserialize json values during communication with node REST endpoint. */
- private final ObjectMapper mapper;
-
- /**
- * Creates new configuration client.
- *
- * @param httpClient Http client.
- */
- @Inject
- public ConfigurationClient(HttpClient httpClient) {
- this.httpClient = httpClient;
- mapper = new ObjectMapper();
- }
-
- /**
- * Gets server node configuration as a raw JSON string.
- *
- * @param host String representation of server node host.
- * @param port Host REST port.
- * @param rawHoconPath HOCON dot-delimited path of requested configuration.
- * @param type Configuration type: {@code node} or {@code cluster}.
- * @return JSON string with node configuration.
- */
- //TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
- public String get(
- String host,
- int port,
- @Nullable String rawHoconPath,
- String type
- ) {
- var req = HttpRequest
- .newBuilder()
- .header("Content-Type", "text/plain");
-
- if (rawHoconPath == null) {
- req.uri(URI.create("http://" + host + ":" + port + GET_URL + type + "/"));
- } else {
- req.uri(URI.create("http://" + host + ":" + port + GET_URL + type + "/" + rawHoconPath));
- }
-
- try {
- HttpResponse<String> res =
- httpClient.send(req.build(),
- HttpResponse.BodyHandlers.ofString());
-
- if (res.statusCode() == HttpURLConnection.HTTP_OK) {
- return mapper.writerWithDefaultPrettyPrinter()
- .writeValueAsString(mapper.readValue(res.body(), JsonNode.class));
- } else {
- throw error("Can't get configuration", res);
- }
- } catch (IOException | InterruptedException e) {
- throw new IgniteCliException("Connection issues while trying to send http request");
- }
- }
-
- /**
- * Sets node configuration from JSON string with configs.
- *
- * @param host String representation of server node host.
- * @param port Host REST port.
- * @param rawHoconData Valid HOCON represented as a string.
- * @param out PrintWriter for printing user messages.
- * @param cs ColorScheme to enrich user messages.
- * @param type Configuration type: {@code node} or {@code cluster}.
- */
- //TODO: Fix in https://issues.apache.org/jira/browse/IGNITE-15306
- public void set(String host, int port, String rawHoconData, PrintWriter out, ColorScheme cs, String type) {
- var req = HttpRequest
- .newBuilder()
- .method("PATCH", HttpRequest.BodyPublishers.ofString(renderJsonFromHocon(rawHoconData)))
- .header("Content-Type", "text/plain")
- .uri(URI.create("http://" + host + ":" + port + PATCH_URL + type + "/"))
- .build();
-
- try {
- HttpResponse<String> res = httpClient.send(req, HttpResponse.BodyHandlers.ofString());
-
- if (res.statusCode() == HttpURLConnection.HTTP_OK) {
- out.println("Configuration was updated successfully.");
- out.println();
- out.println("Use the " + cs.commandText("ignite config get")
- + " command to view the updated configuration.");
- } else {
- throw error("Failed to set configuration", res);
- }
- } catch (IOException | InterruptedException e) {
- throw new IgniteCliException("Connection issues while trying to send http request", e);
- }
- }
-
- /**
- * Prepares exception with message, enriched by HTTP response details.
- *
- * @param msg Base error message.
- * @param res Http response, which cause the raising exce[tion.
- * @return Exception with detailed message.
- * @throws JsonProcessingException if response has incorrect error format.
- */
- private IgniteCliException error(String msg, HttpResponse<String> res) throws JsonProcessingException {
- var errorMsg = mapper.writerWithDefaultPrettyPrinter()
- .writeValueAsString(mapper.readValue(res.body(), JsonNode.class));
-
- return new IgniteCliException(msg + System.lineSeparator().repeat(2) + errorMsg);
- }
-
- /**
- * Produces JSON representation of any valid HOCON string.
- *
- * @param rawHoconData HOCON string.
- * @return JSON representation of HOCON string.
- */
- private static String renderJsonFromHocon(String rawHoconData) {
- return ConfigFactory.parseString(rawHoconData)
- .root().render(ConfigRenderOptions.concise());
- }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
similarity index 60%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
index 178725423..fb7a4810e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigCall.java
@@ -15,27 +15,25 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.builtins.config;
+package org.apache.ignite.cli.call.cliconfig;
-import io.micronaut.context.annotation.Factory;
+import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import java.net.http.HttpClient;
+import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.call.EmptyCallInput;
/**
- * Factory for producing simple HTTP clients.
+ * Gets entire CLI configuration.
*/
-@Factory
-public class HttpClientFactory {
- /**
- * Creates new HTTP client.
- *
- * @return HttpClient
- */
- @Singleton
- HttpClient httpClient() {
- return HttpClient
- .newBuilder()
- .version(HttpClient.Version.HTTP_1_1)
- .build();
+@Singleton
+public class CliConfigCall implements Call<EmptyCallInput, Config> {
+ @Inject
+ private Config config;
+
+ @Override
+ public DefaultCallOutput<Config> execute(EmptyCallInput input) {
+ return DefaultCallOutput.success(config);
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
similarity index 57%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
index 9099a22db..3b72ceb8e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigGetCall.java
@@ -15,36 +15,27 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.call.cliconfig;
-import io.micronaut.core.annotation.Introspected;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.call.StringCallInput;
/**
- * Version provider for Picocli interactions.
+ * Gets CLI configuration parameter.
*/
@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
-
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
+public class CliConfigGetCall implements Call<StringCallInput, String> {
@Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
+ private Config config;
- /** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public DefaultCallOutput<String> execute(StringCallInput input) {
+ String key = input.getString();
+ String property = config.getProperty(key, "");
+ return DefaultCallOutput.success(property);
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCall.java
similarity index 57%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCall.java
index 9099a22db..b2c5a3647 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCall.java
@@ -15,36 +15,30 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.call.cliconfig;
-import io.micronaut.core.annotation.Introspected;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import java.util.Map.Entry;
+import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
/**
- * Version provider for Picocli interactions.
+ * Sets CLI configuration parameters.
*/
@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
-
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
+public class CliConfigSetCall implements Call<CliConfigSetCallInput, Object> {
@Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
+ private Config config;
- /** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public DefaultCallOutput<Object> execute(CliConfigSetCallInput input) {
+ for (Entry<String, String> entry : input.getParameters().entrySet()) {
+ config.setProperty(entry.getKey(), entry.getValue());
+ }
+ config.saveConfig();
+
+ return DefaultCallOutput.empty();
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
similarity index 64%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
index 32fbdbfdb..9e8ac8683 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/cliconfig/CliConfigSetCallInput.java
@@ -15,22 +15,22 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.call.cliconfig;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import java.util.Map;
+import org.apache.ignite.cli.core.call.CallInput;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Input for {@link CliConfigSetCall}.
*/
-public abstract class NoOpHandler extends Handler {
- @Override
- public void flush() {
- // no-op
+public class CliConfigSetCallInput implements CallInput {
+ private final Map<String, String> parameters;
+
+ public CliConfigSetCallInput(Map<String, String> parameters) {
+ this.parameters = parameters;
}
- @Override
- public void close() throws SecurityException {
- // no-op
+ public Map<String, String> getParameters() {
+ return parameters;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigShowCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigShowCall.java
new file mode 100644
index 000000000..28cc526c5
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigShowCall.java
@@ -0,0 +1,58 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.exception.CommandExecutionException;
+import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+
+/**
+ * Shows cluster configuration.
+ */
+@Singleton
+public class ClusterConfigShowCall implements Call<ClusterConfigShowCallInput, String> {
+
+ /** {@inheritDoc} */
+ @Override
+ public DefaultCallOutput<String> execute(ClusterConfigShowCallInput clusterConfigShowCallInput) {
+ ClusterConfigurationApi client = createApiClient(clusterConfigShowCallInput);
+
+ try {
+ return DefaultCallOutput.success(readClusterConfig(client, clusterConfigShowCallInput));
+ } catch (ApiException e) {
+ return DefaultCallOutput.failure(e);
+ } catch (IllegalArgumentException e) {
+ return DefaultCallOutput.failure(new CommandExecutionException("cluster config show", e.getMessage()));
+ }
+ }
+
+ private String readClusterConfig(ClusterConfigurationApi api, ClusterConfigShowCallInput input) throws ApiException {
+ return input.getSelector() != null ? api.getClusterConfigurationByPath(input.getSelector()) : api.getClusterConfiguration();
+ }
+
+ private ClusterConfigurationApi createApiClient(ClusterConfigShowCallInput input) {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(input.getClusterUrl());
+ return new ClusterConfigurationApi(client);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigShowCallInput.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigShowCallInput.java
new file mode 100644
index 000000000..87ba85633
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigShowCallInput.java
@@ -0,0 +1,87 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import org.apache.ignite.cli.core.call.CallInput;
+
+/**
+ * Input for {@link ClusterConfigShowCall}.
+ */
+public class ClusterConfigShowCallInput implements CallInput {
+ /**
+ * Selector for configuration tree.
+ */
+ private final String selector;
+
+ /**
+ * Cluster url.
+ */
+ private final String clusterUrl;
+
+ private ClusterConfigShowCallInput(String selector, String clusterUrl) {
+ this.selector = selector;
+ this.clusterUrl = clusterUrl;
+ }
+
+ /**
+ * Builder for {@link ClusterConfigShowCallInput}.
+ */
+ public static ShowConfigurationCallInputBuilder builder() {
+ return new ShowConfigurationCallInputBuilder();
+ }
+
+ /**
+ * Get selector.
+ *
+ * @return Selector for configuration tree.
+ */
+ public String getSelector() {
+ return selector;
+ }
+
+ /**
+ * Get cluster URL.
+ *
+ * @return Cluster URL.
+ */
+ public String getClusterUrl() {
+ return clusterUrl;
+ }
+
+ /**
+ * Builder for {@link ClusterConfigShowCallInput}.
+ */
+ public static class ShowConfigurationCallInputBuilder {
+ private String selector;
+ private String clusterUrl;
+
+ public ShowConfigurationCallInputBuilder selector(String selector) {
+ this.selector = selector;
+ return this;
+ }
+
+ public ShowConfigurationCallInputBuilder clusterUrl(String clusterUrl) {
+ this.clusterUrl = clusterUrl;
+ return this;
+ }
+
+ public ClusterConfigShowCallInput build() {
+ return new ClusterConfigShowCallInput(selector, clusterUrl);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCall.java
new file mode 100644
index 000000000..6a19a6523
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCall.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.ignite.cli.call.configuration;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.exception.CommandExecutionException;
+import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Updates cluster configuration.
+ */
+@Singleton
+public class ClusterConfigUpdateCall implements Call<ClusterConfigUpdateCallInput, String> {
+ /** {@inheritDoc} */
+ @Override
+ public DefaultCallOutput<String> execute(ClusterConfigUpdateCallInput clusterConfigUpdateCallInput) {
+ ClusterConfigurationApi client = createApiClient(clusterConfigUpdateCallInput);
+
+ try {
+ return updateClusterConfig(client, clusterConfigUpdateCallInput);
+ } catch (ApiException e) {
+ if (e.getCode() == 400) {
+ return DefaultCallOutput.failure(
+ new CommandExecutionException(
+ "cluster config update",
+ "Got error while updating the cluster configuration. " + System.lineSeparator()
+ + "Code: " + e.getCode() + ", response: " + e.getResponseBody()
+ ));
+ }
+ return DefaultCallOutput.failure(new CommandExecutionException("cluster config update", "Ignite api return " + e.getCode()));
+ }
+ }
+
+ private DefaultCallOutput<String> updateClusterConfig(ClusterConfigurationApi api, ClusterConfigUpdateCallInput input)
+ throws ApiException {
+ api.updateClusterConfiguration(input.getConfig());
+ return DefaultCallOutput.success("Cluster configuration was updated successfully.");
+ }
+
+ @NotNull
+ private ClusterConfigurationApi createApiClient(ClusterConfigUpdateCallInput input) {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(input.getClusterUrl());
+ return new ClusterConfigurationApi(client);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCallInput.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCallInput.java
new file mode 100644
index 000000000..d0250cd53
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCallInput.java
@@ -0,0 +1,91 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import org.apache.ignite.cli.core.call.CallInput;
+
+/**
+ * Input for {@link NodeConfigUpdateCall}.
+ */
+public class ClusterConfigUpdateCallInput implements CallInput {
+ /**
+ * Configuration to update.
+ */
+ private final String config;
+
+ /**
+ * Cluster url.
+ */
+ private final String clusterUrl;
+
+ private ClusterConfigUpdateCallInput(String config, String clusterUrl) {
+ this.config = config;
+ this.clusterUrl = clusterUrl;
+ }
+
+ /**
+ * Builder method.
+ *
+ * @return Builder for {@link ClusterConfigUpdateCallInput}.
+ */
+ public static UpdateConfigurationCallInputBuilder builder() {
+ return new UpdateConfigurationCallInputBuilder();
+ }
+
+ /**
+ * Get configuration.
+ *
+ * @return Configuration to update.
+ */
+ public String getConfig() {
+ return config;
+ }
+
+ /**
+ * Get cluster URL.
+ *
+ * @return Cluster url.
+ */
+ public String getClusterUrl() {
+ return clusterUrl;
+ }
+
+ /**
+ * Builder for {@link ClusterConfigUpdateCallInput}.
+ */
+ public static class UpdateConfigurationCallInputBuilder {
+
+ private String config;
+
+ private String clusterUrl;
+
+ public UpdateConfigurationCallInputBuilder config(String config) {
+ this.config = config;
+ return this;
+ }
+
+ public UpdateConfigurationCallInputBuilder clusterUrl(String clusterUrl) {
+ this.clusterUrl = clusterUrl;
+ return this;
+ }
+
+ public ClusterConfigUpdateCallInput build() {
+ return new ClusterConfigUpdateCallInput(config, clusterUrl);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigShowCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigShowCall.java
new file mode 100644
index 000000000..b3f7e8fa8
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigShowCall.java
@@ -0,0 +1,58 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.exception.CommandExecutionException;
+import org.apache.ignite.rest.client.api.NodeConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+
+/**
+ * Shows node configuration from ignite cluster.
+ */
+@Singleton
+public class NodeConfigShowCall implements Call<NodeConfigShowCallInput, String> {
+
+ /** {@inheritDoc} */
+ @Override
+ public DefaultCallOutput<String> execute(NodeConfigShowCallInput readConfigurationInput) {
+ NodeConfigurationApi client = createApiClient(readConfigurationInput);
+
+ try {
+ return DefaultCallOutput.success(readNodeConfig(client, readConfigurationInput));
+ } catch (ApiException e) {
+ return DefaultCallOutput.failure(e);
+ } catch (IllegalArgumentException e) {
+ return DefaultCallOutput.failure(new CommandExecutionException("node config show", e.getMessage()));
+ }
+ }
+
+ private String readNodeConfig(NodeConfigurationApi api, NodeConfigShowCallInput input) throws ApiException {
+ return input.getSelector() != null ? api.getNodeConfigurationByPath(input.getSelector()) : api.getNodeConfiguration();
+ }
+
+ private NodeConfigurationApi createApiClient(NodeConfigShowCallInput input) {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(input.getNodeUrl());
+ return new NodeConfigurationApi(client);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigShowCallInput.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigShowCallInput.java
new file mode 100644
index 000000000..c3b83acd4
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigShowCallInput.java
@@ -0,0 +1,89 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import org.apache.ignite.cli.core.call.CallInput;
+
+/**
+ * Input for {@link NodeConfigShowCall}.
+ */
+public class NodeConfigShowCallInput implements CallInput {
+ /**
+ * Selector for configuration tree.
+ */
+ private final String selector;
+
+ /**
+ * Node url.
+ */
+ private final String nodeUrl;
+
+ private NodeConfigShowCallInput(String selector, String nodeUrl) {
+ this.nodeUrl = nodeUrl;
+ this.selector = selector;
+ }
+
+ /**
+ * Builder for {@link NodeConfigShowCallInput}.
+ */
+ public static ShowConfigurationCallInputBuilder builder() {
+ return new ShowConfigurationCallInputBuilder();
+ }
+
+ /**
+ * Get selector.
+ *
+ * @return Selector for configuration tree.
+ */
+ public String getSelector() {
+ return selector;
+ }
+
+ /**
+ * Get node URL.
+ *
+ * @return Cluster URL.
+ */
+ public String getNodeUrl() {
+ return nodeUrl;
+ }
+
+ /**
+ * Builder for {@link NodeConfigShowCallInput}.
+ */
+ public static class ShowConfigurationCallInputBuilder {
+
+ private String selector;
+
+ private String nodeUrl;
+
+ public ShowConfigurationCallInputBuilder selector(String selector) {
+ this.selector = selector;
+ return this;
+ }
+
+ public ShowConfigurationCallInputBuilder nodeUrl(String nodeUrl) {
+ this.nodeUrl = nodeUrl;
+ return this;
+ }
+
+ public NodeConfigShowCallInput build() {
+ return new NodeConfigShowCallInput(selector, nodeUrl);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCall.java
new file mode 100644
index 000000000..c073e5310
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCall.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.ignite.cli.call.configuration;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.exception.CommandExecutionException;
+import org.apache.ignite.rest.client.api.NodeConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Updates configuration for node.
+ */
+@Singleton
+public class NodeConfigUpdateCall implements Call<NodeConfigUpdateCallInput, String> {
+ /** {@inheritDoc} */
+ @Override
+ public DefaultCallOutput<String> execute(NodeConfigUpdateCallInput nodeConfigUpdateCallInput) {
+ NodeConfigurationApi client = createApiClient(nodeConfigUpdateCallInput);
+
+ try {
+ return updateNodeConfig(client, nodeConfigUpdateCallInput);
+ } catch (ApiException e) {
+ if (e.getCode() == 400) {
+ return DefaultCallOutput.failure(
+ new CommandExecutionException(
+ "node config update",
+ "Got error while updating the node configuration. " + System.lineSeparator()
+ + "Code: " + e.getCode() + ", response: " + e.getResponseBody()
+ ));
+ }
+ return DefaultCallOutput.failure(new CommandExecutionException("node config update", "Ignite api return " + e.getCode()));
+ }
+ }
+
+ private DefaultCallOutput<String> updateNodeConfig(NodeConfigurationApi api, NodeConfigUpdateCallInput input)
+ throws ApiException {
+ api.updateNodeConfiguration(input.getConfig());
+ return DefaultCallOutput.success("Node configuration was updated successfully.");
+ }
+
+ @NotNull
+ private NodeConfigurationApi createApiClient(NodeConfigUpdateCallInput input) {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(input.getNodeUrl());
+ return new NodeConfigurationApi(client);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCallInput.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCallInput.java
new file mode 100644
index 000000000..dae58ac93
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCallInput.java
@@ -0,0 +1,91 @@
+/*
+ * 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.ignite.cli.call.configuration;
+
+import org.apache.ignite.cli.core.call.CallInput;
+
+/**
+ * Input for {@link NodeConfigUpdateCall}.
+ */
+public class NodeConfigUpdateCallInput implements CallInput {
+ /**
+ * Configuration to update.
+ */
+ private final String config;
+
+ /**
+ * Cluster url.
+ */
+ private final String nodeUrl;
+
+ private NodeConfigUpdateCallInput(String config, String clusterUrl) {
+ this.config = config;
+ this.nodeUrl = clusterUrl;
+ }
+
+ /**
+ * Builder method.
+ *
+ * @return Builder for {@link NodeConfigUpdateCallInput}.
+ */
+ public static UpdateConfigurationCallInputBuilder builder() {
+ return new UpdateConfigurationCallInputBuilder();
+ }
+
+ /**
+ * Get configuration.
+ *
+ * @return Configuration to update.
+ */
+ public String getConfig() {
+ return config;
+ }
+
+ /**
+ * Get cluster URL.
+ *
+ * @return Cluster url.
+ */
+ public String getNodeUrl() {
+ return nodeUrl;
+ }
+
+ /**
+ * Builder for {@link NodeConfigUpdateCallInput}.
+ */
+ public static class UpdateConfigurationCallInputBuilder {
+
+ private String config;
+
+ private String nodeUrl;
+
+ public UpdateConfigurationCallInputBuilder config(String config) {
+ this.config = config;
+ return this;
+ }
+
+ public UpdateConfigurationCallInputBuilder nodeUrl(String clusterUrl) {
+ this.nodeUrl = clusterUrl;
+ return this;
+ }
+
+ public NodeConfigUpdateCallInput build() {
+ return new NodeConfigUpdateCallInput(config, nodeUrl);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCall.java
new file mode 100644
index 000000000..270fb5cc8
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCall.java
@@ -0,0 +1,86 @@
+/*
+ * 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.ignite.cli.call.connect;
+
+import com.google.gson.Gson;
+import jakarta.inject.Singleton;
+import java.net.ConnectException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.exception.ConnectCommandException;
+import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.cli.core.repl.config.RootConfig;
+import org.apache.ignite.rest.client.api.NodeConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+import org.jetbrains.annotations.NotNull;
+
+
+/**
+ * Call for connect to Ignite 3 node. As a result {@link Session} will hold a valid node-url.
+ */
+@Singleton
+public class ConnectCall implements Call<ConnectCallInput, String> {
+
+ private final Session session;
+
+ public ConnectCall(Session session) {
+ this.session = session;
+ }
+
+ @Override
+ public CallOutput<String> execute(ConnectCallInput input) {
+ NodeConfigurationApi api = createApiClient(input);
+ String nodeUrl = input.getNodeUrl();
+ try {
+ String configuration = api.getNodeConfiguration();
+ setJdbcUrl(configuration, nodeUrl);
+ } catch (ApiException e) {
+ session.setConnectedToNode(false);
+ if (e.getCause() instanceof ConnectException) {
+ return DefaultCallOutput.failure(new ConnectCommandException("Can not connect to " + input.getNodeUrl()));
+ }
+ return DefaultCallOutput.failure(e);
+ }
+
+ session.setNodeUrl(nodeUrl);
+ session.setConnectedToNode(true);
+ return DefaultCallOutput.success("Connected to " + nodeUrl);
+ }
+
+ @NotNull
+ private NodeConfigurationApi createApiClient(ConnectCallInput input) {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(input.getNodeUrl());
+ return new NodeConfigurationApi(client);
+ }
+
+ private void setJdbcUrl(String configuration, String nodeUrl) {
+ try {
+ String host = new URL(nodeUrl).getHost();
+ RootConfig config = new Gson().fromJson(configuration, RootConfig.class);
+ session.setJdbcUrl("jdbc:ignite:thin://" + host + ":" + config.clientConnector.port);
+ } catch (MalformedURLException ignored) {
+ // Shouldn't happen ever since we are now connected to this URL
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCallInput.java
similarity index 50%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCallInput.java
index 9099a22db..6f22136ba 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCallInput.java
@@ -15,36 +15,41 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.call.connect;
-import io.micronaut.core.annotation.Introspected;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import org.apache.ignite.cli.core.call.CallInput;
/**
- * Version provider for Picocli interactions.
+ * Input for connect call to Ignite 3 node.
*/
-@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
+public class ConnectCallInput implements CallInput {
+ private final String nodeUrl;
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
+ ConnectCallInput(String nodeUrl) {
+ this.nodeUrl = nodeUrl;
+ }
+
+ public String getNodeUrl() {
+ return nodeUrl;
+ }
+
+ public static ConnectCallInputBuilder builder() {
+ return new ConnectCallInputBuilder();
+ }
/**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
+ * Builder for {@link ConnectCall}.
*/
- @Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
+ public static class ConnectCallInputBuilder {
+ private String nodeUrl;
+
+ public ConnectCallInputBuilder nodeUrl(String nodeUrl) {
+ this.nodeUrl = nodeUrl;
+ return this;
+ }
- /** {@inheritDoc} */
- @Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public ConnectCallInput build() {
+ return new ConnectCallInput(nodeUrl);
+ }
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/DisconnectCall.java
similarity index 51%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/call/connect/DisconnectCall.java
index 9099a22db..4bb5b2d77 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/DisconnectCall.java
@@ -15,36 +15,39 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.call.connect;
-import io.micronaut.core.annotation.Introspected;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.call.EmptyCallInput;
+import org.apache.ignite.cli.core.repl.Session;
/**
- * Version provider for Picocli interactions.
+ * Call for disconnect.
*/
@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
+public class DisconnectCall implements Call<EmptyCallInput, String> {
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
@Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
+ private final Session session;
+
+ public DisconnectCall(Session session) {
+ this.session = session;
}
- /** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public CallOutput<String> execute(EmptyCallInput input) {
+ if (session.isConnectedToNode()) {
+ String nodeUrl = session.getNodeUrl();
+ session.setNodeUrl(null);
+ session.setConnectedToNode(false);
+
+ return DefaultCallOutput.success("Disconnected from " + nodeUrl);
+ }
+
+ return DefaultCallOutput.empty();
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/status/Status.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/status/Status.java
new file mode 100644
index 000000000..45528ac41
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/status/Status.java
@@ -0,0 +1,117 @@
+/*
+ * 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.ignite.cli.call.status;
+
+/**
+ * Class that represents the cluster status.
+ */
+public class Status {
+
+ private final int nodeCount;
+
+ private final boolean initialized;
+
+ private final String name;
+
+ private final boolean connected;
+
+ private final String connectedNodeUrl;
+
+ private Status(int nodeCount, boolean initialized, String name, boolean connected, String connectedNodeUrl) {
+ this.nodeCount = nodeCount;
+ this.initialized = initialized;
+ this.name = name;
+ this.connected = connected;
+ this.connectedNodeUrl = connectedNodeUrl;
+ }
+
+ public int getNodeCount() {
+ return nodeCount;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public String getConnectedNodeUrl() {
+ return connectedNodeUrl;
+ }
+
+ /**
+ * Builder for {@link Status}.
+ */
+ public static StatusBuilder builder() {
+ return new StatusBuilder();
+ }
+
+ /**
+ * Builder for {@link Status}.
+ */
+ public static class StatusBuilder {
+ private int nodeCount;
+
+ private boolean initialized;
+
+ private String name;
+
+ private boolean connected;
+
+ private String connectedNodeUrl;
+
+ private StatusBuilder() {
+
+ }
+
+ public StatusBuilder nodeCount(int nodeCount) {
+ this.nodeCount = nodeCount;
+ return this;
+ }
+
+ public StatusBuilder initialized(boolean initialized) {
+ this.initialized = initialized;
+ return this;
+ }
+
+ public StatusBuilder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public StatusBuilder connected(boolean connected) {
+ this.connected = connected;
+ return this;
+ }
+
+ public StatusBuilder connectedNodeUrl(String connectedNodeUrl) {
+ this.connectedNodeUrl = connectedNodeUrl;
+ return this;
+ }
+
+ public Status build() {
+ return new Status(nodeCount, initialized, name, connected, connectedNodeUrl);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/status/StatusCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/status/StatusCall.java
new file mode 100644
index 000000000..294d249f9
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/status/StatusCall.java
@@ -0,0 +1,99 @@
+/*
+ * 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.ignite.cli.call.status;
+
+import jakarta.inject.Singleton;
+import java.net.ConnectException;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.call.StatusCallInput;
+import org.apache.ignite.cli.core.exception.CommandExecutionException;
+import org.apache.ignite.cli.deprecated.CliPathsConfigLoader;
+import org.apache.ignite.cli.deprecated.IgnitePaths;
+import org.apache.ignite.cli.deprecated.builtins.node.NodeManager;
+import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
+import org.apache.ignite.rest.client.api.NodeConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+
+/**
+ * Call to get cluster status.
+ */
+@Singleton
+//TODO: https://issues.apache.org/jira/browse/IGNITE-17093
+public class StatusCall implements Call<StatusCallInput, Status> {
+
+ private final NodeManager nodeManager;
+
+ private final CliPathsConfigLoader cliPathsCfgLdr;
+
+ /**
+ * Default constructor.
+ */
+ public StatusCall(NodeManager nodeManager, CliPathsConfigLoader cliPathsCfgLdr) {
+ this.nodeManager = nodeManager;
+ this.cliPathsCfgLdr = cliPathsCfgLdr;
+ }
+
+ @Override
+ public CallOutput<Status> execute(StatusCallInput input) {
+ IgnitePaths paths = cliPathsCfgLdr.loadIgnitePathsOrThrowError();
+ String connected = null;
+ try {
+ connected = createNodeApi(input).getNodeConfiguration();
+ } catch (ApiException e) {
+ if (e.getCause() instanceof ConnectException) {
+ return DefaultCallOutput.failure(new CommandExecutionException("status", "cannot connect to " + input.getClusterUrl()));
+ } else {
+ return DefaultCallOutput.failure(e);
+ }
+ }
+ return DefaultCallOutput.success(
+ Status.builder()
+ .connected(connected != null)
+ .connectedNodeUrl(input.getClusterUrl())
+ .initialized(connected != null && canReadClusterConfig(input))
+ .nodeCount(nodeManager.getRunningNodes(paths.logDir, paths.cliPidsDir()).size())
+ .build()
+ );
+ }
+
+ private boolean canReadClusterConfig(StatusCallInput input) {
+ var clusterApi = createClusterApi(input);
+ try {
+ clusterApi.getClusterConfiguration();
+ return true;
+ } catch (ApiException e) {
+ return false;
+ }
+ }
+
+ private ClusterConfigurationApi createClusterApi(StatusCallInput input) {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(input.getClusterUrl());
+ return new ClusterConfigurationApi(client);
+ }
+
+ private NodeConfigurationApi createNodeApi(StatusCallInput input) {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(input.getClusterUrl());
+ return new NodeConfigurationApi(client);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/status/StatusReplCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/status/StatusReplCall.java
new file mode 100644
index 000000000..a7c8babcf
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/status/StatusReplCall.java
@@ -0,0 +1,86 @@
+/*
+ * 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.ignite.cli.call.status;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.call.EmptyCallInput;
+import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.cli.deprecated.CliPathsConfigLoader;
+import org.apache.ignite.cli.deprecated.IgnitePaths;
+import org.apache.ignite.cli.deprecated.builtins.node.NodeManager;
+import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.invoker.Configuration;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Call to get cluster status.
+ */
+@Singleton
+//TODO: https://issues.apache.org/jira/browse/IGNITE-17093
+public class StatusReplCall implements Call<EmptyCallInput, Status> {
+
+ private final NodeManager nodeManager;
+
+ private final CliPathsConfigLoader cliPathsCfgLdr;
+
+ private final Session session;
+
+ /**
+ * Default constructor.
+ */
+ public StatusReplCall(NodeManager nodeManager, CliPathsConfigLoader cliPathsCfgLdr, Session session) {
+ this.nodeManager = nodeManager;
+ this.cliPathsCfgLdr = cliPathsCfgLdr;
+ this.session = session;
+ }
+
+ @Override
+ public CallOutput<Status> execute(EmptyCallInput input) {
+ IgnitePaths paths = cliPathsCfgLdr.loadIgnitePathsOrThrowError();
+ return DefaultCallOutput.success(
+ Status.builder()
+ .connected(session.isConnectedToNode())
+ .connectedNodeUrl(session.getNodeUrl())
+ .initialized(session.isConnectedToNode() && canReadClusterConfig())
+ .nodeCount(nodeManager.getRunningNodes(paths.logDir, paths.cliPidsDir()).size())
+ .build()
+ );
+ }
+
+ private boolean canReadClusterConfig() {
+ var clusterApi = createApiClient();
+ try {
+ clusterApi.getClusterConfiguration();
+ return true;
+ } catch (ApiException e) {
+ return false;
+ }
+ }
+
+ @NotNull
+ private ClusterConfigurationApi createApiClient() {
+ ApiClient client = Configuration.getDefaultApiClient();
+ client.setBasePath(session.getNodeUrl());
+ return new ClusterConfigurationApi(client);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/BaseCommand.java
similarity index 67%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/BaseCommand.java
index 2e0050be3..46f8b4b26 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/BaseCommand.java
@@ -15,15 +15,24 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands;
-import picocli.CommandLine;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Spec;
/**
- * Base class for commands without any subcommands.
+ * Base class for commands.
*/
-public abstract class CommandSpec extends SpecAdapter {
+public abstract class BaseCommand implements Runnable {
/** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
+ @Option(names = {"-h", "--help"}, usageHelp = true, description = "Show this help message and exit.")
protected boolean usageHelpRequested;
+
+ @Spec
+ protected CommandSpec spec;
+
+ @Override
+ public void run() {
+ }
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/TopLevelCliCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/TopLevelCliCommand.java
new file mode 100644
index 000000000..040523bee
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/TopLevelCliCommand.java
@@ -0,0 +1,60 @@
+/*
+ * 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.ignite.cli.commands;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.VersionProvider;
+import org.apache.ignite.cli.commands.cliconfig.CliCommand;
+import org.apache.ignite.cli.commands.configuration.cluster.ClusterCommand;
+import org.apache.ignite.cli.commands.configuration.node.NodeCommand;
+import org.apache.ignite.cli.commands.sql.SqlCommand;
+import org.apache.ignite.cli.commands.status.StatusCommand;
+import org.apache.ignite.cli.deprecated.spec.BootstrapIgniteCommandSpec;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Top-level command that prints help and declares subcommands.
+ */
+@Command(name = "ignite",
+ versionProvider = VersionProvider.class,
+ description = {
+ "Welcome to Ignite Shell alpha.",
+ "Run without command to enter interactive mode.",
+ ""},
+ subcommands = {
+ SqlCommand.class,
+ CommandLine.HelpCommand.class,
+ StatusCommand.class,
+ CliCommand.class,
+ BootstrapIgniteCommandSpec.class,
+ NodeCommand.class,
+ ClusterCommand.class
+ })
+@Singleton
+public class TopLevelCliCommand extends BaseCommand {
+ @SuppressWarnings("PMD.UnusedPrivateField")
+ @Option(names = {"--version"}, versionHelp = true, description = "Print version information and exit")
+ private boolean versionRequested;
+
+ @Override
+ public void run() {
+
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/TopLevelCliReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/TopLevelCliReplCommand.java
new file mode 100644
index 000000000..94638253e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/TopLevelCliReplCommand.java
@@ -0,0 +1,53 @@
+/*
+ * 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.ignite.cli.commands;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.commands.cliconfig.CliCommand;
+import org.apache.ignite.cli.commands.configuration.cluster.ClusterReplCommand;
+import org.apache.ignite.cli.commands.configuration.node.NodeReplCommand;
+import org.apache.ignite.cli.commands.connect.ConnectCommand;
+import org.apache.ignite.cli.commands.connect.DisconnectCommand;
+import org.apache.ignite.cli.commands.sql.SqlCommand;
+import org.apache.ignite.cli.commands.status.StatusReplCommand;
+import org.apache.ignite.cli.commands.version.VersionCommand;
+import org.apache.ignite.cli.deprecated.spec.BootstrapIgniteCommandSpec;
+import picocli.CommandLine;
+import picocli.shell.jline3.PicocliCommands;
+
+/**
+ * Top-level command that just prints help.
+ */
+@CommandLine.Command(name = "",
+ footer = {"", "Press Ctrl-D to exit."},
+ subcommands = {
+ SqlCommand.class,
+ PicocliCommands.ClearScreen.class,
+ CommandLine.HelpCommand.class,
+ VersionCommand.class,
+ StatusReplCommand.class,
+ CliCommand.class,
+ BootstrapIgniteCommandSpec.class,
+ ConnectCommand.class,
+ DisconnectCommand.class,
+ NodeReplCommand.class,
+ ClusterReplCommand.class
+ })
+@Singleton
+public class TopLevelCliReplCommand {
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliCommand.java
similarity index 65%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliCommand.java
index 2e0050be3..da91cb11e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliCommand.java
@@ -15,15 +15,20 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.cliconfig;
-import picocli.CommandLine;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.commands.BaseCommand;
+import picocli.CommandLine.Command;
/**
- * Base class for commands without any subcommands.
+ * Parent command for CLI configuration commands.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@Command(name = "cli",
+ description = "CLI specific commands",
+ subcommands = {
+ CliConfigSubCommand.class
+ })
+@Singleton
+public class CliCommand extends BaseCommand {
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
similarity index 50%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
index 9099a22db..7f33bfdd5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
@@ -15,36 +15,34 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.cliconfig;
-import io.micronaut.core.annotation.Introspected;
import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import org.apache.ignite.cli.call.cliconfig.CliConfigGetCall;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.StringCallInput;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
/**
- * Version provider for Picocli interactions.
+ * Command to get CLI configuration parameters.
*/
-@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
+@Command(name = "get")
+public class CliConfigGetSubCommand extends BaseCommand {
+ @Parameters
+ private String key;
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
@Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
+ private CliConfigGetCall call;
- /** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public void run() {
+ CallExecutionPipeline.builder(call)
+ .inputProvider(() -> new StringCallInput(key))
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
new file mode 100644
index 000000000..137a41fd2
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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.ignite.cli.commands.cliconfig;
+
+import jakarta.inject.Inject;
+import java.util.Map;
+import org.apache.ignite.cli.call.cliconfig.CliConfigSetCall;
+import org.apache.ignite.cli.call.cliconfig.CliConfigSetCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Parameters;
+
+/**
+ * Command to set CLI configuration parameters.
+ */
+@Command(name = "set")
+public class CliConfigSetSubCommand extends BaseCommand {
+ @Parameters(arity = "1..*")
+ private Map<String, String> parameters;
+
+ @Inject
+ private CliConfigSetCall call;
+
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(call)
+ .inputProvider(() -> new CliConfigSetCallInput(parameters))
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
new file mode 100644
index 000000000..095518504
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
@@ -0,0 +1,52 @@
+/*
+ * 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.ignite.cli.commands.cliconfig;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.cliconfig.CliConfigCall;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.ConfigDecorator;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.EmptyCallInput;
+import picocli.CommandLine.Command;
+
+/**
+ * Parent command for CLI configuration commands.
+ */
+@Command(name = "config", subcommands = {
+ CliConfigGetSubCommand.class,
+ CliConfigSetSubCommand.class
+})
+@Singleton
+public class CliConfigSubCommand extends BaseCommand {
+
+ @Inject
+ private CliConfigCall call;
+
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(call)
+ .inputProvider(EmptyCallInput::new)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .decorator(new ConfigDecorator())
+ .build()
+ .runPipeline();
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterCommand.java
similarity index 65%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterCommand.java
index 32fbdbfdb..5b4d0e19c 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterCommand.java
@@ -15,22 +15,20 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.configuration.cluster;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.deprecated.spec.ClusterCommandSpec;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Node command.
*/
-public abstract class NoOpHandler extends Handler {
- @Override
- public void flush() {
- // no-op
- }
+@Command(name = "cluster",
+ subcommands = {ClusterConfigSubCommand.class},
+ description = "Cluster config operations.")
+public class ClusterCommand {
- @Override
- public void close() throws SecurityException {
- // no-op
- }
+ @Mixin
+ ClusterCommandSpec clusterCommandSpec;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigReplSubCommand.java
similarity index 69%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigReplSubCommand.java
index 2e0050be3..c8d639a92 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigReplSubCommand.java
@@ -15,15 +15,16 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.configuration.cluster;
-import picocli.CommandLine;
+import picocli.CommandLine.Command;
/**
- * Base class for commands without any subcommands.
+ * Node config command in REPL.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@Command(name = "config",
+ subcommands = {ClusterConfigShowReplSubCommand.class, ClusterConfigUpdateReplSubCommand.class},
+ description = "Cluster config operations.")
+public class ClusterConfigReplSubCommand {
+
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigShowReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigShowReplSubCommand.java
new file mode 100644
index 000000000..65fd49b95
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigShowReplSubCommand.java
@@ -0,0 +1,100 @@
+/*
+ * 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.ignite.cli.commands.configuration.cluster;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.call.configuration.ClusterConfigShowCall;
+import org.apache.ignite.cli.call.configuration.ClusterConfigShowCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.JsonDecorator;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Command that shows configuration from the cluster in REPL mode.
+ */
+@Command(name = "show",
+ description = "Shows cluster configuration.")
+public class ClusterConfigShowReplSubCommand extends BaseCommand {
+
+ /**
+ * Configuration selector option.
+ */
+ @Option(names = {"--selector"}, description = "Configuration path selector.")
+ private String selector;
+
+ /**
+ * Node url option.
+ */
+ @Option(
+ names = {"--cluster-url"}, description = "Url to Ignite node."
+ )
+ private String clusterUrl;
+
+ @Inject
+ private ClusterConfigShowCall call;
+
+ @Inject
+ private Session session;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ var input = ClusterConfigShowCallInput.builder().selector(selector);
+ if (session.isConnectedToNode()) {
+ input.clusterUrl(session.getNodeUrl());
+ } else if (clusterUrl != null) {
+ input.clusterUrl(clusterUrl);
+ } else {
+ spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '--cluster-url' option.");
+ return;
+ }
+
+ CallExecutionPipeline.builder(call)
+ .inputProvider(input::build)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .decorator(new JsonDecorator())
+ .exceptionHandler(new ShowConfigReplExceptionHandler())
+ .build()
+ .runPipeline();
+ }
+
+ private static class ShowConfigReplExceptionHandler implements ExceptionHandler<ApiException> {
+ @Override
+ public void handle(ExceptionWriter err, ApiException e) {
+ if (e.getCode() == 500) { //TODO: https://issues.apache.org/jira/browse/IGNITE-17091
+ err.write("Cannot show cluster config, probably you have not initialized the cluster. "
+ + "Try to run 'cluster init' command.");
+ return;
+ }
+
+ err.write(e.getResponseBody());
+ }
+
+ @Override
+ public Class<ApiException> applicableException() {
+ return ApiException.class;
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigShowSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigShowSubCommand.java
new file mode 100644
index 000000000..29548c47f
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigShowSubCommand.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ignite.cli.commands.configuration.cluster;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.call.configuration.ClusterConfigShowCall;
+import org.apache.ignite.cli.call.configuration.ClusterConfigShowCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.JsonDecorator;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Command that shows configuration from the cluster.
+ */
+@Command(name = "show",
+ description = "Shows cluster configuration.")
+public class ClusterConfigShowSubCommand extends BaseCommand {
+
+ /**
+ * Configuration selector option.
+ */
+ @Option(names = {"--selector"}, description = "Configuration path selector.")
+ private String selector;
+
+ /**
+ * Node url option.
+ */
+ @Option(
+ names = {"--cluster-url"}, description = "Url to ignite node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String clusterUrl;
+
+ @Inject
+ private ClusterConfigShowCall call;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(call)
+ .inputProvider(this::buildCallInput)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .decorator(new JsonDecorator())
+ .build()
+ .runPipeline();
+ }
+
+ private ClusterConfigShowCallInput buildCallInput() {
+ return ClusterConfigShowCallInput.builder()
+ .clusterUrl(clusterUrl)
+ .selector(selector)
+ .build();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigSubCommand.java
similarity index 70%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigSubCommand.java
index 2e0050be3..4eaaa56e7 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigSubCommand.java
@@ -15,15 +15,16 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.configuration.cluster;
-import picocli.CommandLine;
+import picocli.CommandLine.Command;
/**
- * Base class for commands without any subcommands.
+ * Node config command.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@Command(name = "config",
+ subcommands = {ClusterConfigShowSubCommand.class, ClusterConfigUpdateSubCommand.class},
+ description = "Cluster config operations.")
+public class ClusterConfigSubCommand {
+
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigUpdateReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigUpdateReplSubCommand.java
new file mode 100644
index 000000000..91ada9d0e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigUpdateReplSubCommand.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ignite.cli.commands.configuration.cluster;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.configuration.ClusterConfigUpdateCall;
+import org.apache.ignite.cli.call.configuration.ClusterConfigUpdateCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.repl.Session;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+/**
+ * Command that updates cluster configuration in REPL mode.
+ */
+@Command(name = "update",
+ description = "Updates cluster configuration.")
+@Singleton
+public class ClusterConfigUpdateReplSubCommand extends BaseCommand {
+ /**
+ * Cluster url option.
+ */
+ @Option(
+ names = {"--cluster-url"}, description = "Url to Ignite node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String clusterUrl;
+
+ /**
+ * Configuration that will be updated.
+ */
+ @Parameters(index = "0")
+ private String config;
+
+ @Inject
+ ClusterConfigUpdateCall call;
+
+ @Inject
+ private Session session;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ var input = ClusterConfigUpdateCallInput.builder().config(config);
+ if (session.isConnectedToNode()) {
+ input.clusterUrl(session.getNodeUrl());
+ } else if (clusterUrl != null) {
+ input.clusterUrl(clusterUrl);
+ } else {
+ spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '--cluster-url' option.");
+ return;
+ }
+
+ CallExecutionPipeline.builder(this.call)
+ .inputProvider(input::build)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigUpdateSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigUpdateSubCommand.java
new file mode 100644
index 000000000..12aafaf0e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterConfigUpdateSubCommand.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ignite.cli.commands.configuration.cluster;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.configuration.ClusterConfigUpdateCall;
+import org.apache.ignite.cli.call.configuration.ClusterConfigUpdateCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+/**
+ * Command that updates cluster configuration.
+ */
+@Command(name = "update",
+ description = "Updates cluster configuration.")
+@Singleton
+public class ClusterConfigUpdateSubCommand extends BaseCommand {
+ @Inject
+ ClusterConfigUpdateCall call;
+
+ /**
+ * Cluster url option.
+ */
+ @Option(
+ names = {"--cluster-url"}, description = "Url to Ignite node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String clusterUrl;
+
+ /**
+ * Configuration that will be updated.
+ */
+ @Parameters(index = "0")
+ private String config;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(call)
+ .inputProvider(this::buildCallInput)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
+ }
+
+ private ClusterConfigUpdateCallInput buildCallInput() {
+ return ClusterConfigUpdateCallInput.builder()
+ .clusterUrl(clusterUrl)
+ .config(config)
+ .build();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterReplCommand.java
similarity index 63%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterReplCommand.java
index 2e0050be3..9b0e8d1ac 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/cluster/ClusterReplCommand.java
@@ -15,15 +15,19 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.configuration.cluster;
-import picocli.CommandLine;
+import org.apache.ignite.cli.deprecated.spec.ClusterReplCommandSpec;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
/**
- * Base class for commands without any subcommands.
+ * Cluster command in REPL mode.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@Command(name = "cluster",
+ subcommands = {ClusterConfigReplSubCommand.class},
+ description = "Cluster config operations.")
+public class ClusterReplCommand {
+ @Mixin
+ ClusterReplCommandSpec clusterReplCommandSpec;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeCommand.java
similarity index 66%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeCommand.java
index 2e0050be3..435214dfb 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeCommand.java
@@ -15,15 +15,20 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.configuration.node;
-import picocli.CommandLine;
+import org.apache.ignite.cli.deprecated.spec.NodeCommandSpec;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
/**
- * Base class for commands without any subcommands.
+ * Node command.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@Command(name = "node",
+ subcommands = {NodeConfigSubCommand.class},
+ description = "Node config operations.")
+public class NodeCommand {
+
+ @Mixin
+ NodeCommandSpec nodeCommandSpec;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigReplSubCommand.java
similarity index 70%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigReplSubCommand.java
index 2e0050be3..ebbf3c78a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigReplSubCommand.java
@@ -15,15 +15,16 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.configuration.node;
-import picocli.CommandLine;
+import picocli.CommandLine.Command;
/**
- * Base class for commands without any subcommands.
+ * Node config command in REPL mode.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@Command(name = "config",
+ subcommands = {NodeConfigShowReplSubCommand.class, NodeConfigUpdateReplSubCommand.class},
+ description = "Node config operations.")
+public class NodeConfigReplSubCommand {
+
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigShowReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigShowReplSubCommand.java
new file mode 100644
index 000000000..8cb43510f
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigShowReplSubCommand.java
@@ -0,0 +1,100 @@
+/*
+ * 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.ignite.cli.commands.configuration.node;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.call.configuration.NodeConfigShowCall;
+import org.apache.ignite.cli.call.configuration.NodeConfigShowCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.JsonDecorator;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Command that shows node configuration from the cluster in REPL mode.
+ */
+@Command(name = "show",
+ description = "Shows node configuration.")
+public class NodeConfigShowReplSubCommand extends BaseCommand {
+
+ /**
+ * Configuration selector option.
+ */
+ @Option(names = {"--selector"}, description = "Configuration path selector.")
+ private String selector;
+
+ /**
+ * Node url option.
+ */
+ @Option(
+ names = {"--node-url"}, description = "Url to Ignite node."
+ )
+ private String nodeUrl;
+
+ @Inject
+ private NodeConfigShowCall call;
+
+ @Inject
+ private Session session;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ var input = NodeConfigShowCallInput.builder().selector(selector);
+ if (session.isConnectedToNode()) {
+ input.nodeUrl(session.getNodeUrl());
+ } else if (nodeUrl != null) {
+ input.nodeUrl(nodeUrl);
+ } else {
+ spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '--node-url' option.");
+ return;
+ }
+
+ CallExecutionPipeline.builder(call)
+ .inputProvider(input::build)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .decorator(new JsonDecorator())
+ .exceptionHandler(new ShowConfigReplExceptionHandler())
+ .build()
+ .runPipeline();
+ }
+
+ private static class ShowConfigReplExceptionHandler implements ExceptionHandler<ApiException> {
+ @Override
+ public void handle(ExceptionWriter err, ApiException e) {
+ if (e.getCode() == 500) { //TODO: https://issues.apache.org/jira/browse/IGNITE-17091
+ err.write("Cannot show node config, probably you have not initialized the cluster. "
+ + "Try to run 'cluster init' command.");
+ return;
+ }
+
+ err.write(e.getResponseBody());
+ }
+
+ @Override
+ public Class<ApiException> applicableException() {
+ return ApiException.class;
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigShowSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigShowSubCommand.java
new file mode 100644
index 000000000..56a8de470
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigShowSubCommand.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ignite.cli.commands.configuration.node;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.call.configuration.NodeConfigShowCall;
+import org.apache.ignite.cli.call.configuration.NodeConfigShowCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.JsonDecorator;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Command that shows configuration from the cluster.
+ */
+@Command(name = "show",
+ description = "Shows node configuration.")
+public class NodeConfigShowSubCommand extends BaseCommand {
+
+ /**
+ * Configuration selector option.
+ */
+ @Option(names = {"--selector"}, description = "Configuration path selector.")
+ private String selector;
+
+ /**
+ * Node url option.
+ */
+ @Option(
+ names = {"--node-url"}, description = "Url to ignite node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String nodeUrl;
+
+ @Inject
+ private NodeConfigShowCall call;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(call)
+ .inputProvider(this::buildCallInput)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .decorator(new JsonDecorator())
+ .build()
+ .runPipeline();
+ }
+
+ private NodeConfigShowCallInput buildCallInput() {
+ return NodeConfigShowCallInput.builder()
+ .nodeUrl(nodeUrl)
+ .selector(selector)
+ .build();
+ }
+}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigSubCommand.java
similarity index 71%
copy from modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigSubCommand.java
index 7387f57aa..8be82912f 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigSubCommand.java
@@ -15,19 +15,16 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.configuration.node;
-import org.junit.jupiter.api.BeforeAll;
+import picocli.CommandLine.Command;
/**
- * Base class for any CLI tests.
+ * Node config command.
*/
-public abstract class AbstractCliTest {
- /**
- * Sets up a dumb terminal before tests.
- */
- @BeforeAll
- static void beforeAll() {
- System.setProperty("org.jline.terminal.dumb", "true");
- }
+@Command(name = "config",
+ subcommands = {NodeConfigShowSubCommand.class, NodeConfigUpdateSubCommand.class},
+ description = "Node config operations.")
+public class NodeConfigSubCommand {
+
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigUpdateReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigUpdateReplSubCommand.java
new file mode 100644
index 000000000..6d3116084
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigUpdateReplSubCommand.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ignite.cli.commands.configuration.node;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.configuration.NodeConfigUpdateCall;
+import org.apache.ignite.cli.call.configuration.NodeConfigUpdateCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.repl.Session;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+/**
+ * Command that updates configuration in REPL mode.
+ */
+@Command(name = "update",
+ description = "Updates node configuration.")
+@Singleton
+public class NodeConfigUpdateReplSubCommand extends BaseCommand {
+ /**
+ * Node url option.
+ */
+ @Option(
+ names = {"--node-url"}, description = "Url to Ignite node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String nodeUrl;
+
+ /**
+ * Configuration that will be updated.
+ */
+ @Parameters(index = "0")
+ private String config;
+
+ @Inject
+ NodeConfigUpdateCall call;
+
+ @Inject
+ private Session session;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ var input = NodeConfigUpdateCallInput.builder().config(config);
+ if (session.isConnectedToNode()) {
+ input.nodeUrl(session.getNodeUrl());
+ } else if (nodeUrl != null) {
+ input.nodeUrl(nodeUrl);
+ } else {
+ spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '--cluster-url' option.");
+ return;
+ }
+
+ CallExecutionPipeline.builder(this.call)
+ .inputProvider(input::build)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigUpdateSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigUpdateSubCommand.java
new file mode 100644
index 000000000..08a3125e5
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeConfigUpdateSubCommand.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ignite.cli.commands.configuration.node;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.configuration.NodeConfigUpdateCall;
+import org.apache.ignite.cli.call.configuration.NodeConfigUpdateCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Parameters;
+
+/**
+ * Command that updates node configuration.
+ */
+@Command(name = "update",
+ description = "Updates node configuration.")
+@Singleton
+public class NodeConfigUpdateSubCommand extends BaseCommand {
+ @Inject
+ NodeConfigUpdateCall call;
+ /**
+ * Node url option.
+ */
+ @Option(
+ names = {"--node-url"}, description = "Url to Ignite node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String nodeUrl;
+ /**
+ * Configuration that will be updated.
+ */
+ @Parameters(index = "0")
+ private String config;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(call)
+ .inputProvider(this::buildCallInput)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
+ }
+
+ private NodeConfigUpdateCallInput buildCallInput() {
+ return NodeConfigUpdateCallInput.builder()
+ .nodeUrl(nodeUrl)
+ .config(config)
+ .build();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeReplCommand.java
similarity index 65%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeReplCommand.java
index 2e0050be3..f8c30a5c5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/configuration/node/NodeReplCommand.java
@@ -15,15 +15,19 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.configuration.node;
-import picocli.CommandLine;
+import org.apache.ignite.cli.deprecated.spec.NodeCommandSpec;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
/**
- * Base class for commands without any subcommands.
+ * Node command in REPL mode.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@Command(name = "node",
+ subcommands = {NodeConfigReplSubCommand.class},
+ description = "Node config operations.")
+public class NodeReplCommand {
+ @Mixin
+ NodeCommandSpec nodeCommandSpec;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
new file mode 100644
index 000000000..32a6e534a
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
@@ -0,0 +1,63 @@
+/*
+ * 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.ignite.cli.commands.connect;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.connect.ConnectCall;
+import org.apache.ignite.cli.call.connect.ConnectCallInput;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Spec;
+
+/**
+ * Connects to the Ignite 3 node.
+ */
+@Command(name = "connect", description = "Connect to Ignite 3 node.")
+@Singleton
+public class ConnectCommand extends BaseCommand {
+
+ /**
+ * Cluster url option.
+ */
+ @Parameters(
+ description = "Ignite node url.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String nodeUrl;
+
+ @Spec
+ private CommandSpec spec;
+
+ @Inject
+ private ConnectCall connectCall;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(connectCall)
+ .inputProvider(() -> ConnectCallInput.builder().nodeUrl(nodeUrl).build())
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/DisconnectCommand.java
similarity index 50%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/DisconnectCommand.java
index 9099a22db..588989507 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/DisconnectCommand.java
@@ -15,36 +15,38 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.connect;
-import io.micronaut.core.annotation.Introspected;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import org.apache.ignite.cli.call.connect.DisconnectCall;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.EmptyCallInput;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Spec;
/**
- * Version provider for Picocli interactions.
+ * Connects to the Ignite 3 node.
*/
+@Command(name = "disconnect", description = "Disconnect from Ignite 3 node.")
@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
+public class DisconnectCommand extends BaseCommand {
+ @Spec
+ private CommandSpec spec;
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
@Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
+ private DisconnectCall disconnectCall;
/** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public void run() {
+ CallExecutionPipeline.builder(disconnectCall)
+ .inputProvider(EmptyCallInput::new)
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ConfigDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ConfigDecorator.java
new file mode 100644
index 000000000..87c91adf5
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ConfigDecorator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.ignite.cli.commands.decorators;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
+import org.apache.ignite.cli.config.Config;
+
+/**
+ * Decorator for printing {@link Config}.
+ */
+public class ConfigDecorator implements Decorator<Config, TerminalOutput> {
+ @Override
+ public TerminalOutput decorate(Config data) {
+ StringBuilder builder = new StringBuilder();
+ for (Iterator<Entry<Object, Object>> iterator = data.getProperties().entrySet().iterator(); iterator.hasNext(); ) {
+ Entry<Object, Object> entry = iterator.next();
+ builder.append(entry.getKey()).append("=").append(entry.getValue());
+ if (iterator.hasNext()) {
+ builder.append(System.lineSeparator());
+ }
+ }
+ return builder::toString;
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
similarity index 65%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
index 32fbdbfdb..a9a8270f7 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
@@ -15,22 +15,21 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.decorators;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Default decorator that calls toString method.
+ *
+ * @param <I> Input type.
*/
-public abstract class NoOpHandler extends Handler {
- @Override
- public void flush() {
- // no-op
- }
+public class DefaultDecorator<I> implements Decorator<I, TerminalOutput> {
+ /** {@inheritDoc} */
@Override
- public void close() throws SecurityException {
- // no-op
+ public TerminalOutput decorate(I data) {
+ return data::toString;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/JsonDecorator.java
similarity index 50%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/JsonDecorator.java
index 9099a22db..de90a79e0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/JsonDecorator.java
@@ -15,36 +15,29 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.decorators;
-import io.micronaut.core.annotation.Introspected;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
/**
- * Version provider for Picocli interactions.
+ * Pretty json decorator.
*/
-@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
-
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
- @Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
+public class JsonDecorator implements Decorator<String, TerminalOutput> {
/** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public TerminalOutput decorate(String jsonString) {
+ ObjectMapper mapper = new ObjectMapper();
+ return () -> {
+ try {
+ return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readValue(jsonString, JsonNode.class));
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ };
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/SqlQueryResultDecorator.java
similarity index 55%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/SqlQueryResultDecorator.java
index dc90c1881..d4713838a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/SqlQueryResultDecorator.java
@@ -15,24 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.decorators;
-import java.io.PrintWriter;
-import picocli.CommandLine.Help.ColorScheme;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
+import org.apache.ignite.cli.sql.SqlQueryResult;
/**
- * Base specification for commands which have subcommands.
+ * Composite decorator for {@link SqlQueryResult}.
*/
-public abstract class CategorySpec extends SpecAdapter {
- /** {@inheritDoc} */
- @Override
- public void run() {
- PrintWriter out = spec.commandLine().getOut();
- ColorScheme cs = spec.commandLine().getColorScheme();
+public class SqlQueryResultDecorator implements Decorator<SqlQueryResult, TerminalOutput> {
+
+ private final TableDecorator tableDecorator = new TableDecorator();
- out.println(cs.errorText("[ERROR] ") + "Unknown command: "
- + cs.commandText(spec.qualifiedName()) + ". See the list of available commands below.\n");
+ private final DefaultDecorator<String> messageDecorator = new DefaultDecorator<>();
- spec.parent().commandLine().usage(out);
+ @Override
+ public TerminalOutput decorate(SqlQueryResult data) {
+ return data.getResult(tableDecorator, messageDecorator);
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/StatusDecorator.java
similarity index 50%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/StatusDecorator.java
index 9099a22db..d874f8eaf 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/StatusDecorator.java
@@ -15,36 +15,26 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.decorators;
-import io.micronaut.core.annotation.Introspected;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import org.apache.ignite.cli.call.status.Status;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
+import picocli.CommandLine.Help.Ansi;
/**
- * Version provider for Picocli interactions.
+ * Decorator for {@link Status}.
*/
-@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
+public class StatusDecorator implements Decorator<Status, TerminalOutput> {
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
- @Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
-
- /** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public TerminalOutput decorate(Status data) {
+ if (!data.isConnected()) {
+ return () -> "Can not get status from " + data.getConnectedNodeUrl();
+ }
+
+ return () -> Ansi.AUTO.string("Status from " + data.getConnectedNodeUrl() + System.lineSeparator()
+ + "[nodes: " + data.getNodeCount() + ", status: "
+ + (data.isInitialized() ? "@|fg(10) active|@" : "@|fg(9) not initialized|@") + "]");
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/StatusReplDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/StatusReplDecorator.java
new file mode 100644
index 000000000..3740f639e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/StatusReplDecorator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.ignite.cli.commands.decorators;
+
+import org.apache.ignite.cli.call.status.Status;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
+import picocli.CommandLine.Help.Ansi;
+
+/**
+ * Decorator for {@link Status}.
+ */
+public class StatusReplDecorator implements Decorator<Status, TerminalOutput> {
+
+ @Override
+ public TerminalOutput decorate(Status data) {
+ if (!data.isConnected()) {
+ return () -> "You are not connected to any Ignite 3 node. Try to run 'connect' command.";
+ }
+
+ return () -> Ansi.AUTO.string("Connected to " + data.getConnectedNodeUrl() + System.lineSeparator()
+ + "[nodes: " + data.getNodeCount() + ", status: "
+ + (data.isInitialized() ? "@|fg(10) active|@" : "@|fg(9) not initialized|@") + "]");
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TableDecorator.java
similarity index 53%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TableDecorator.java
index 9099a22db..71407824d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TableDecorator.java
@@ -15,36 +15,26 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.commands.decorators;
-import io.micronaut.core.annotation.Introspected;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import com.jakewharton.fliptables.FlipTableConverters;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
+import org.apache.ignite.cli.sql.table.Table;
/**
- * Version provider for Picocli interactions.
+ * Implementation of {@link Decorator} for {@link Table}.
*/
-@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
-
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
+public class TableDecorator implements Decorator<Table<String>, TerminalOutput> {
/**
- * Creates version provider.
+ * Transform {@link Table} to {@link TerminalOutput}.
*
- * @param cliVerInfo Actual Ignite CLI version container.
+ * @param table incoming {@link Table}.
+ * @return User friendly interpretation of {@link Table} in {@link TerminalOutput}.
*/
- @Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
- }
-
- /** {@inheritDoc} */
@Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public TerminalOutput decorate(Table<String> table) {
+ return () -> FlipTableConverters.fromObjects(table.header(), table.content());
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/core/Decorator.java
similarity index 61%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/core/Decorator.java
index 178725423..79fc19d4d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/core/Decorator.java
@@ -15,27 +15,20 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.builtins.config;
-
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-import java.net.http.HttpClient;
+package org.apache.ignite.cli.commands.decorators.core;
/**
- * Factory for producing simple HTTP clients.
+ * Interface for transformation command output to terminal output.
+ *
+ * @param <CommandDataT> type of command output.
+ * @param <TerminalDataT> type of terminal output.
*/
-@Factory
-public class HttpClientFactory {
+public interface Decorator<CommandDataT, TerminalDataT extends TerminalOutput> {
/**
- * Creates new HTTP client.
+ * Interface for transforming command output to terminal output.
*
- * @return HttpClient
+ * @param data incoming data object.
+ * @return Decorated object with type {@link TerminalDataT}.
*/
- @Singleton
- HttpClient httpClient() {
- return HttpClient
- .newBuilder()
- .version(HttpClient.Version.HTTP_1_1)
- .build();
- }
+ TerminalDataT decorate(CommandDataT data);
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/core/TerminalOutput.java
similarity index 74%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/core/TerminalOutput.java
index 5cb93f287..eb9899e92 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/core/TerminalOutput.java
@@ -15,8 +15,16 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.commands.decorators.core;
+
/**
- * Contains classes for Ignite module management.
+ * Interface for terminal output representation.
*/
-
-package org.apache.ignite.cli.builtins.module;
+public interface TerminalOutput {
+ /**
+ * Terminal output transformation.
+ *
+ * @return String representation of some
+ */
+ String toTerminalString();
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCommand.java
new file mode 100644
index 000000000..986f4e2a8
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCommand.java
@@ -0,0 +1,125 @@
+/*
+ * 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.ignite.cli.commands.sql;
+
+import jakarta.inject.Inject;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.sql.SQLException;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.SqlQueryResultDecorator;
+import org.apache.ignite.cli.core.CallExecutionPipelineProvider;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.StringCallInput;
+import org.apache.ignite.cli.core.exception.CommandExecutionException;
+import org.apache.ignite.cli.core.exception.ExceptionHandlers;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.exception.handler.DefaultExceptionHandlers;
+import org.apache.ignite.cli.core.exception.handler.SqlExceptionHandler;
+import org.apache.ignite.cli.core.repl.Repl;
+import org.apache.ignite.cli.core.repl.executor.RegistryCommandExecutor;
+import org.apache.ignite.cli.core.repl.executor.ReplExecutorProvider;
+import org.apache.ignite.cli.core.repl.executor.SqlQueryCall;
+import org.apache.ignite.cli.sql.SqlManager;
+import org.apache.ignite.cli.sql.SqlSchemaProvider;
+import org.jetbrains.annotations.NotNull;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Command for sql execution.
+ */
+@Command(name = "sql", description = "Executes SQL query.")
+public class SqlCommand extends BaseCommand {
+ private static final String INTERNAL_COMMAND_PREFIX = "!";
+
+ @Option(names = {"-u", "--jdbc-url"}, required = true,
+ descriptionKey = "ignite.jdbc-url", description = "JDBC url to ignite cluster")
+ private String jdbc;
+
+ @Option(names = {"-e", "--execute", "--exec"}) //todo: can be passed as parameter, not option (see IEP-88)
+ private String command;
+
+ @Option(names = {"-f", "--script-file"})
+ private File file;
+
+ @Inject
+ private ReplExecutorProvider replExecutorProvider;
+
+ private static String extract(File file) {
+ try {
+ return String.join("\n", Files.readAllLines(file.toPath(), StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ throw new CommandExecutionException("sql", "File with command not found.");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void run() {
+ try (SqlManager sqlManager = new SqlManager(jdbc)) {
+ if (command == null && file == null) {
+ replExecutorProvider.get().execute(Repl.builder()
+ .withPromptProvider(() -> "sql-cli> ")
+ .withCompleter(new SqlCompleter(new SqlSchemaProvider(sqlManager::getMetadata)))
+ .withCommandClass(SqlReplTopLevelCliCommand.class)
+ .withCallExecutionPipelineProvider(provider(sqlManager))
+ .withHistoryFileName("sqlhistory")
+ .build());
+ } else {
+ String executeCommand = file != null ? extract(file) : command;
+ createSqlExecPipeline(sqlManager, executeCommand).runPipeline();
+ }
+ } catch (SQLException e) {
+ new SqlExceptionHandler().handle(ExceptionWriter.fromPrintWriter(spec.commandLine().getErr()), e);
+ }
+ }
+
+ @NotNull
+ private CallExecutionPipelineProvider provider(SqlManager sqlManager) {
+ return (call, exceptionHandlers, line) -> line.startsWith(INTERNAL_COMMAND_PREFIX)
+ ? createInternalCommandPipeline(call, exceptionHandlers, line)
+ : createSqlExecPipeline(sqlManager, line);
+ }
+
+ private CallExecutionPipeline<?, ?> createSqlExecPipeline(SqlManager sqlManager, String line) {
+ return CallExecutionPipeline.builder(new SqlQueryCall(sqlManager))
+ .inputProvider(() -> new StringCallInput(line))
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .exceptionHandlers(new DefaultExceptionHandlers())
+ .decorator(new SqlQueryResultDecorator())
+ .build();
+ }
+
+ private CallExecutionPipeline<?, ?> createInternalCommandPipeline(RegistryCommandExecutor call,
+ ExceptionHandlers exceptionHandlers,
+ String line) {
+ return CallExecutionPipeline.builder(call)
+ .inputProvider(() -> new StringCallInput(line.substring(INTERNAL_COMMAND_PREFIX.length())))
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .exceptionHandlers(new DefaultExceptionHandlers())
+ .exceptionHandlers(exceptionHandlers)
+ .build();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCompleter.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCompleter.java
new file mode 100644
index 000000000..23fa9768d
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCompleter.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ignite.cli.commands.sql;
+
+import java.util.List;
+import org.apache.ignite.cli.sql.SchemaProvider;
+import org.jline.reader.Candidate;
+import org.jline.reader.Completer;
+import org.jline.reader.LineReader;
+import org.jline.reader.ParsedLine;
+
+class SqlCompleter implements Completer {
+ private final SchemaProvider schemaProvider;
+
+ SqlCompleter(SchemaProvider schemaProvider) {
+ this.schemaProvider = schemaProvider;
+ }
+
+ @Override
+ public void complete(LineReader reader, ParsedLine commandLine, List<Candidate> candidates) {
+ if (commandLine.wordIndex() == 0) {
+ addCandidatesFromArray(SqlMetaData.STARTING_KEYWORDS, candidates);
+ } else {
+ fillCandidates(candidates);
+ }
+ }
+
+ private void fillCandidates(List<Candidate> candidates) {
+ addKeywords(candidates);
+ for (String schema : schemaProvider.getSchema().schemas()) {
+ addCandidate(schema, candidates);
+ for (String table : schemaProvider.getSchema().tables(schema)) {
+ addCandidate(table, candidates);
+ //TODO: https://issues.apache.org/jira/browse/IGNITE-16973
+ }
+ }
+ }
+
+ private void addKeywords(List<Candidate> candidates) {
+ addCandidatesFromArray(SqlMetaData.KEYWORDS, candidates);
+ addCandidatesFromArray(SqlMetaData.NUMERIC_FUNCTIONS, candidates);
+ addCandidatesFromArray(SqlMetaData.STRING_FUNCTIONS, candidates);
+ addCandidatesFromArray(SqlMetaData.TIME_DATE_FUNCTIONS, candidates);
+ addCandidatesFromArray(SqlMetaData.SYSTEM_FUNCTIONS, candidates);
+ }
+
+ private static void addCandidatesFromArray(String[] strings, List<Candidate> candidates) {
+ for (String keyword : strings) {
+ addCandidate(keyword, candidates);
+ }
+ }
+
+ private static void addCandidate(String string, List<Candidate> candidates) {
+ candidates.add(new Candidate(string));
+ candidates.add(new Candidate(string.toLowerCase()));
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlMetaData.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlMetaData.java
new file mode 100644
index 000000000..70b227a45
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlMetaData.java
@@ -0,0 +1,96 @@
+/*
+ * 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.ignite.cli.commands.sql;
+
+/**
+ * Constants for SQL auto-completion.
+ */
+
+public class SqlMetaData {
+
+ /**
+ * SQL reserved keywords.
+ */
+ public static String[] KEYWORDS = { //TODO: https://issues.apache.org/jira/browse/IGNITE-16973
+ "ABSOLUTE", "ACTION", "ADD", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", "ARE", "AS", "ASC", "ASSERTION", "AT", "AUTHORIZATION",
+ "AVG", "BEGIN", "BETWEEN", "BIT", "BIT_LENGTH", "BOTH", "BY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAR",
+ "CHARACTER", "CHAR_LENGTH", "CHARACTER_LENGTH", "CHECK", "CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COMMIT",
+ "CONNECT", "CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONTINUE", "CONVERT", "CORRESPONDING", "COUNT", "CREATE", "CROSS",
+ "CURRENT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATE", "DAY", "DEALLOCATE", "DEC",
+ "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DESCRIBE", "DESCRIPTOR", "DIAGNOSTICS",
+ "DISCONNECT", "DISTINCT", "DOMAIN", "DOUBLE", "DROP", "ELSE", "END", "END-EXEC", "ESCAPE", "EXCEPT", "EXCEPTION", "EXEC",
+ "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FIRST", "FLOAT", "FOR", "FOREIGN", "FOUND", "FROM", "FULL",
+ "GET", "GLOBAL", "GO", "GOTO", "GRANT", "GROUP", "HAVING", "HOUR", "IDENTITY", "IMMEDIATE", "IN", "INDICATOR", "INITIALLY",
+ "INNER", "INPUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTERSECT", "INTERVAL", "INTO", "IS", "ISOLATION", "JOIN", "KEY",
+ "LANGUAGE", "LAST", "LEADING", "LEFT", "LEVEL", "LIKE", "LOCAL", "LOWER", "MATCH", "MAX", "MIN", "MINUTE", "MODULE", "MONTH",
+ "NAMES", "NATIONAL", "NATURAL", "NCHAR", "NEXT", "NO", "NOT", "NULL", "NULLIF", "NUMERIC", "OCTET_LENGTH", "OF", "ON", "ONLY",
+ "OPEN", "OPTION", "OR", "ORDER", "OUTER", "OUTPUT", "OVERLAPS", "PAD", "PARTIAL", "POSITION", "PRECISION", "PREPARE",
+ "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURE", "PUBLIC", "READ", "REAL", "REFERENCES", "RELATIVE", "RESTRICT",
+ "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "SCHEMA", "SCROLL", "SECOND", "SECTION", "SELECT", "SESSION", "SESSION_USER", "SET",
+ "SIZE", "SMALLINT", "SOME", "SPACE", "SQL", "SQLCODE", "SQLERROR", "SQLSTATE", "SUBSTRING", "SUM", "SYSTEM_USER", "TABLE",
+ "TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", "TRANSLATE",
+ "TRANSLATION", "TRIM", "TRUE", "UNION", "UNIQUE", "UNKNOWN", "UPDATE", "UPPER", "USAGE", "USER", "USING", "VALUE", "VALUES",
+ "VARCHAR", "VARYING", "VIEW", "WHEN", "WHENEVER", "WHERE", "WITH", "WORK", "WRITE", "YEAR", "ZONE", "ADA", "C", "CATALOG_NAME",
+ "CHARACTER_SET_CATALOG", "CHARACTER_SET_NAME", "CHARACTER_SET_SCHEMA", "CLASS_ORIGIN", "COBOL", "COLLATION_CATALOG",
+ "COLLATION_NAME", "COLLATION_SCHEMA", "COLUMN_NAME", "COMMAND_FUNCTION", "COMMITTED", "CONDITION_NUMBER", "CONNECTION_NAME",
+ "CONSTRAINT_CATALOG", "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CURSOR_NAME", "DATA", "DATETIME_INTERVAL_CODE",
+ "DATETIME_INTERVAL_PRECISION", "DYNAMIC_FUNCTION", "FORTRAN", "LENGTH", "MESSAGE_LENGTH", "MESSAGE_OCTET_LENGTH",
+ "MESSAGE_TEXT", "MORE", "MUMPS", "NAME", "NULLABLE", "NUMBER", "PASCAL", "PLI", "REPEATABLE", "RETURNED_LENGTH",
+ "RETURNED_OCTET_LENGTH", "RETURNED_SQLSTATE", "ROW_COUNT", "SCALE", "SCHEMA_NAME", "SERIALIZABLE", "SERVER_NAME",
+ "SUBCLASS_ORIGIN", "TABLE_NAME", "TYPE", "UNCOMMITTED", "UNNAMED"
+ };
+
+ /**
+ * List of keywords which must be at the beginning of the statement, used for very basic completion.
+ */
+ public static final String[] STARTING_KEYWORDS = {
+ "ALTER", "SET", "CREATE", "DROP", "WITH", "SELECT", "EXPLAIN", "DESCRIBE", "INSERT", "DELETE", "UPDATE", "MERGE", "CALL"
+ };
+
+ // These lists are copied from calcite
+ /**
+ * SQL numeric functions.
+ */
+ public static final String[] NUMERIC_FUNCTIONS = {
+ "ABS", "ACOS", "ASIN", "ATAN", "ATAN2", "CBRT", "CEILING", "COS", "COT", "DEGREES", "EXP", "FLOOR", "LOG", "LOG10", "MOD", "PI",
+ "POWER", "RADIANS", "RAND", "ROUND", "SIGN", "SIN", "SQRT", "TAN", "TRUNCATE"
+ };
+
+ /**
+ * SQL string functions.
+ */
+ public static final String[] STRING_FUNCTIONS = {
+ "ASCII", "CHAR", "CONCAT", "DIFFERENCE", "INSERT", "LCASE", "LEFT", "LENGTH", "LOCATE",
+ "LTRIM", "REPEAT", "REPLACE", "RIGHT", "RTRIM", "SOUNDEX", "SPACE", "SUBSTRING", "UCASE"
+ };
+
+ /**
+ * SQL date/time functions.
+ */
+ public static final String[] TIME_DATE_FUNCTIONS = {
+ "CONVERT_TIMEZONE", "CURDATE", "CURTIME", "DAYNAME", "DAYOFMONTH", "DAYOFWEEK", "DAYOFYEAR", "HOUR", "MINUTE", "MONTH",
+ "MONTHNAME", "NOW", "QUARTER", "SECOND", "TIMESTAMPADD", "TIMESTAMPDIFF", "TO_DATE", "TO_TIMESTAMP", "WEEK", "YEAR"
+ };
+
+ /**
+ * SQL system functions.
+ */
+ public static final String[] SYSTEM_FUNCTIONS = {
+ "CONVERT", "DATABASE", "IFNULL", "USER"
+ };
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplTopLevelCliCommand.java
similarity index 65%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplTopLevelCliCommand.java
index 2e0050be3..661364410 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplTopLevelCliCommand.java
@@ -15,15 +15,22 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.sql;
+import jakarta.inject.Singleton;
import picocli.CommandLine;
+import picocli.shell.jline3.PicocliCommands;
/**
- * Base class for commands without any subcommands.
+ * Top level SQL REPL command.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+@CommandLine.Command(name = "",
+ description = {""},
+ footer = {"", "Press Ctrl-D to exit."},
+ subcommands = {
+ CommandLine.HelpCommand.class,
+ PicocliCommands.ClearScreen.class
+ })
+@Singleton
+public class SqlReplTopLevelCliCommand {
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/status/StatusCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/status/StatusCommand.java
new file mode 100644
index 000000000..c2be4f3aa
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/status/StatusCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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.ignite.cli.commands.status;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.status.StatusCall;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.StatusDecorator;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.StatusCallInput;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Spec;
+
+/**
+ * Command that prints status of ignite cluster.
+ */
+@Command(name = "status",
+ aliases = "cluster show", //TODO: https://issues.apache.org/jira/browse/IGNITE-17102
+ description = "Prints status of the cluster.")
+@Singleton
+public class StatusCommand extends BaseCommand {
+
+ /**
+ * Cluster url option.
+ */
+ @SuppressWarnings("PMD.UnusedPrivateField")
+ @Option(
+ names = {"--cluster-url"}, description = "Url to cluster node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String clusterUrl;
+
+ @Spec
+ private CommandSpec commandSpec;
+
+ @Inject
+ private StatusCall statusCall;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(statusCall)
+ .inputProvider(() -> new StatusCallInput(clusterUrl))
+ .output(commandSpec.commandLine().getOut())
+ .errOutput(commandSpec.commandLine().getErr())
+ .decorator(new StatusDecorator())
+ .build()
+ .runPipeline();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/status/StatusReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/status/StatusReplCommand.java
new file mode 100644
index 000000000..4ebb924ca
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/status/StatusReplCommand.java
@@ -0,0 +1,65 @@
+/*
+ * 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.ignite.cli.commands.status;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.call.status.StatusReplCall;
+import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.commands.decorators.StatusReplDecorator;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.call.EmptyCallInput;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Spec;
+
+/**
+ * Command that prints status of ignite cluster.
+ */
+@Command(name = "status", description = "Prints status of the cluster.")
+@Singleton
+public class StatusReplCommand extends BaseCommand {
+
+ /**
+ * Cluster url option.
+ */
+ @SuppressWarnings("PMD.UnusedPrivateField")
+ @Option(
+ names = {"--cluster-url"}, description = "Url to cluster node."
+ )
+ private String clusterUrl;
+
+ @Spec
+ private CommandSpec commandSpec;
+
+ @Inject
+ private StatusReplCall statusReplCall;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ CallExecutionPipeline.builder(statusReplCall)
+ .inputProvider(EmptyCallInput::new)
+ .output(commandSpec.commandLine().getOut())
+ .errOutput(commandSpec.commandLine().getErr())
+ .decorator(new StatusReplDecorator())
+ .build()
+ .runPipeline();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyCommand.java
new file mode 100644
index 000000000..872238fb6
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyCommand.java
@@ -0,0 +1,53 @@
+/*
+ * 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.ignite.cli.commands.topology;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.commands.BaseCommand;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.Spec;
+
+/**
+ * Command that prints ignite cluster topology.
+ */
+@Command(name = "topology", description = "Prints topology information.")
+@Singleton
+public class TopologyCommand extends BaseCommand {
+
+ /**
+ * Cluster url option.
+ */
+ @SuppressWarnings("PMD.UnusedPrivateField")
+ @Option(
+ names = {"--cluster-url"}, description = "Url to cluster node.",
+ descriptionKey = "ignite.cluster-url", defaultValue = "http://localhost:10300"
+ )
+ private String clusterUrl;
+
+ @Spec
+ private CommandSpec commandSpec;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ //TODO: https://issues.apache.org/jira/browse/IGNITE-17092
+ commandSpec.commandLine().getOut().println("Topology command is not implemented yet.");
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/version/VersionCommand.java
similarity index 58%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/commands/version/VersionCommand.java
index dc90c1881..18f5946e5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CategorySpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/version/VersionCommand.java
@@ -15,24 +15,31 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.commands.version;
-import java.io.PrintWriter;
-import picocli.CommandLine.Help.ColorScheme;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.cli.VersionProvider;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Spec;
/**
- * Base specification for commands which have subcommands.
+ * Command that prints CLI version.
*/
-public abstract class CategorySpec extends SpecAdapter {
+@Command(name = "version", description = "Prints CLI version.")
+@Singleton
+public class VersionCommand implements Runnable {
+
+ @Spec
+ private CommandSpec commandSpec;
+
+ @Inject
+ private VersionProvider versionProvider;
+
/** {@inheritDoc} */
@Override
public void run() {
- PrintWriter out = spec.commandLine().getOut();
- ColorScheme cs = spec.commandLine().getColorScheme();
-
- out.println(cs.errorText("[ERROR] ") + "Unknown command: "
- + cs.commandText(spec.qualifiedName()) + ". See the list of available commands below.\n");
-
- spec.parent().commandLine().usage(out);
+ commandSpec.commandLine().getOut().println(versionProvider.getVersion()[0]);
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/config/Config.java b/modules/cli/src/main/java/org/apache/ignite/cli/config/Config.java
new file mode 100644
index 000000000..04374689b
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/Config.java
@@ -0,0 +1,123 @@
+/*
+ * 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.ignite.cli.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.util.Properties;
+
+/**
+ * CLI default configuration.
+ */
+public class Config {
+ private static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
+ private static final String XDG_STATE_HOME = "XDG_STATE_HOME";
+ private static final String PARENT_FOLDER_NAME = "ignitecli";
+ private static final String CONFIG_FILE_NAME = "defaults";
+
+ private final File configFile;
+ private final Properties props = new Properties();
+
+ public Config(File configFile) {
+ this.configFile = configFile;
+ loadConfig(configFile);
+ }
+
+ /**
+ * Loads config from the default location specified by the XDG_CONFIG_HOME.
+ */
+ public Config() {
+ this(getConfigFile());
+ }
+
+ public Properties getProperties() {
+ return props;
+ }
+
+ public String getProperty(String key) {
+ return props.getProperty(key);
+ }
+
+ public String getProperty(String key, String defaultValue) {
+ return props.getProperty(key, defaultValue);
+ }
+
+ public void setProperty(String key, String value) {
+ props.setProperty(key, value);
+ }
+
+ private void loadConfig(File configFile) {
+ if (configFile.canRead()) {
+ try (InputStream is = new FileInputStream(configFile)) {
+ props.load(is);
+ } catch (IOException e) {
+ // todo report error?
+ }
+ }
+ }
+
+ /**
+ * Saves config to file.
+ */
+ public void saveConfig() {
+ configFile.getParentFile().mkdirs();
+ if (configFile.canWrite()) {
+ try (OutputStream os = new FileOutputStream(configFile)) {
+ props.store(os, null);
+ } catch (IOException e) {
+ // todo report error?
+ }
+ }
+ }
+
+ private static File getConfigFile() {
+ return getConfigRoot().resolve(PARENT_FOLDER_NAME).resolve(CONFIG_FILE_NAME).toFile();
+ }
+
+ /**
+ * Gets the path for the state.
+ *
+ * @return Folder for state storage.
+ */
+ public static File getStateFolder() {
+ return getStateRoot().resolve(PARENT_FOLDER_NAME).toFile();
+ }
+
+ private static Path getConfigRoot() {
+ String xdgConfigHome = System.getenv(XDG_CONFIG_HOME);
+ if (xdgConfigHome != null) {
+ return Path.of(xdgConfigHome);
+ } else {
+ return Path.of(System.getProperty("user.home"), ".config");
+ }
+ }
+
+ private static Path getStateRoot() {
+ String xdgStateHome = System.getenv(XDG_STATE_HOME);
+ if (xdgStateHome != null) {
+ return Path.of(xdgStateHome);
+ } else {
+ return Path.of(System.getProperty("user.home"), ".local", "state");
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/node/package-info.java b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
similarity index 74%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/node/package-info.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
index 0ebfd92cd..a8e09d2f3 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/node/package-info.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/ConfigFactory.java
@@ -15,8 +15,18 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.config;
+
+import io.micronaut.context.annotation.Factory;
+import jakarta.inject.Singleton;
+
/**
- * Contains classes for Ignite node management.
+ * Factory for {@link Config}.
*/
-
-package org.apache.ignite.cli.builtins.node;
+@Factory
+public class ConfigFactory {
+ @Singleton
+ public Config fileConfig() {
+ return new Config();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/CallExecutionPipelineProvider.java
similarity index 56%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/CallExecutionPipelineProvider.java
index 178725423..0c0241174 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/CallExecutionPipelineProvider.java
@@ -15,27 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.builtins.config;
+package org.apache.ignite.cli.core;
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-import java.net.http.HttpClient;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.exception.ExceptionHandlers;
+import org.apache.ignite.cli.core.repl.executor.RegistryCommandExecutor;
/**
- * Factory for producing simple HTTP clients.
+ * Provider of {@link CallExecutionPipeline}.
*/
-@Factory
-public class HttpClientFactory {
+public interface CallExecutionPipelineProvider {
/**
- * Creates new HTTP client.
+ * Pipeline getter.
*
- * @return HttpClient
+ * @param executor default executor.
+ * @param exceptionHandlers exception handlers.
+ * @param line call input.
+ * @return new pipeline {@link CallExecutionPipeline}
*/
- @Singleton
- HttpClient httpClient() {
- return HttpClient
- .newBuilder()
- .version(HttpClient.Version.HTTP_1_1)
- .build();
- }
+ CallExecutionPipeline<?, ?> get(RegistryCommandExecutor executor, ExceptionHandlers exceptionHandlers, String line);
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/Call.java
similarity index 71%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/call/Call.java
index 2e0050be3..6becdbb4c 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/Call.java
@@ -15,15 +15,15 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
-
-import picocli.CommandLine;
+package org.apache.ignite.cli.core.call;
/**
- * Base class for commands without any subcommands.
+ * Call that represents an action that can be performed given an input.
+ * It can be rest call, dictionary lookup or whatever.
+ *
+ * @param <IT> Input for the call.
+ * @param <OT> Output of the call.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+public interface Call<IT extends CallInput, OT> {
+ CallOutput<OT> execute(IT input);
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallExecutionPipeline.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallExecutionPipeline.java
new file mode 100644
index 000000000..578ea02ae
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallExecutionPipeline.java
@@ -0,0 +1,183 @@
+/*
+ * 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.ignite.cli.core.call;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.function.Supplier;
+import org.apache.ignite.cli.commands.decorators.DefaultDecorator;
+import org.apache.ignite.cli.commands.decorators.core.Decorator;
+import org.apache.ignite.cli.commands.decorators.core.TerminalOutput;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionHandlers;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.exception.handler.DefaultExceptionHandlers;
+
+/**
+ * Call execution pipeline.
+ *
+ * @param <I> Call input type.
+ * @param <T> Call output's body type.
+ */
+public class CallExecutionPipeline<I extends CallInput, T> {
+ /**
+ * Call to execute.
+ */
+ private final Call<I, T> call;
+
+ /**
+ * Writer for execution output.
+ */
+ private final PrintWriter output;
+
+ /**
+ * Writer for error execution output.
+ */
+ private final PrintWriter errOutput;
+
+ /**
+ * Decorator that decorates call's output.
+ */
+ private final Decorator<T, TerminalOutput> decorator;
+
+ /**
+ * Handlers for any exceptions.
+ */
+ private final ExceptionHandlers exceptionHandlers;
+
+ /**
+ * Provider for call's input.
+ */
+ private final Supplier<I> inputProvider;
+
+ private CallExecutionPipeline(Call<I, T> call,
+ PrintWriter output,
+ PrintWriter errOutput,
+ ExceptionHandlers exceptionHandlers,
+ Decorator<T, TerminalOutput> decorator,
+ Supplier<I> inputProvider) {
+ this.call = call;
+ this.output = output;
+ this.exceptionHandlers = exceptionHandlers;
+ this.errOutput = errOutput;
+ this.decorator = decorator;
+ this.inputProvider = inputProvider;
+ }
+
+ /**
+ * Builder helper method.
+ *
+ * @return builder for {@link CallExecutionPipeline}.
+ */
+ public static <I extends CallInput, T> CallExecutionPipelineBuilder<I, T> builder(
+ Call<I, T> call) {
+ return new CallExecutionPipelineBuilder<>(call);
+ }
+
+ /** {@inheritDoc} */
+ public void runPipeline() {
+ I callInput = inputProvider.get();
+
+ CallOutput<T> callOutput = call.execute(callInput);
+
+ if (callOutput.hasError()) {
+ exceptionHandlers.handleException(ExceptionWriter.fromPrintWriter(errOutput), callOutput.errorCause());
+ return;
+ }
+
+ if (callOutput.isEmpty()) {
+ return;
+ }
+
+ TerminalOutput decoratedOutput = decorator.decorate(callOutput.body());
+
+ output.println(decoratedOutput.toTerminalString());
+ }
+
+ /** Builder for {@link CallExecutionPipeline}. */
+ public static class CallExecutionPipelineBuilder<I extends CallInput, T> {
+
+ private final Call<I, T> call;
+
+ private final ExceptionHandlers exceptionHandlers = new DefaultExceptionHandlers();
+
+ private Supplier<I> inputProvider;
+
+ private PrintWriter output = wrapOutputStream(System.out);
+
+ private PrintWriter errOutput = wrapOutputStream(System.err);
+
+ private Decorator<T, TerminalOutput> decorator = new DefaultDecorator<>();
+
+ public CallExecutionPipelineBuilder(Call<I, T> call) {
+ this.call = call;
+ }
+
+ public CallExecutionPipelineBuilder<I, T> inputProvider(Supplier<I> inputProvider) {
+ this.inputProvider = inputProvider;
+ return this;
+ }
+
+ public CallExecutionPipelineBuilder<I, T> output(PrintWriter output) {
+ this.output = output;
+ return this;
+ }
+
+ public CallExecutionPipelineBuilder<I, T> output(OutputStream output) {
+ return output(wrapOutputStream(output));
+ }
+
+ public CallExecutionPipelineBuilder<I, T> errOutput(PrintWriter errOutput) {
+ this.errOutput = errOutput;
+ return this;
+ }
+
+ public CallExecutionPipelineBuilder<I, T> errOutput(OutputStream output) {
+ return errOutput(wrapOutputStream(output));
+ }
+
+ public CallExecutionPipelineBuilder<I, T> exceptionHandler(ExceptionHandler<?> exceptionHandler) {
+ exceptionHandlers.addExceptionHandler(exceptionHandler);
+ return this;
+ }
+
+ public CallExecutionPipelineBuilder<I, T> exceptionHandlers(ExceptionHandlers exceptionHandlers) {
+ this.exceptionHandlers.addExceptionHandlers(exceptionHandlers);
+ return this;
+ }
+
+ public CallExecutionPipelineBuilder<I, T> decorator(Decorator<T, TerminalOutput> decorator) {
+ this.decorator = decorator;
+ return this;
+ }
+
+ public CallExecutionPipeline<I, T> build() {
+ return new CallExecutionPipeline<>(call, output, errOutput, exceptionHandlers, decorator, inputProvider);
+ }
+
+ private static PrintWriter wrapOutputStream(OutputStream output) {
+ return new PrintWriter(output, true, getStdoutEncoding());
+ }
+
+ private static Charset getStdoutEncoding() {
+ String encoding = System.getProperty("sun.stdout.encoding");
+ return encoding != null ? Charset.forName(encoding) : Charset.defaultCharset();
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallInput.java
similarity index 82%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallInput.java
index 5cb93f287..46e4b36af 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallInput.java
@@ -15,8 +15,11 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.core.call;
+
/**
- * Contains classes for Ignite module management.
+ * Input for {@link Call}.
+ * Usually this input contains all information needed to execute the call.
*/
-
-package org.apache.ignite.cli.builtins.module;
+public interface CallInput {
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ResolveResult.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallOutput.java
similarity index 56%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ResolveResult.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallOutput.java
index 658f069a9..d4326864c 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ResolveResult.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallOutput.java
@@ -15,34 +15,39 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.builtins.module;
-
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
+package org.apache.ignite.cli.core.call;
/**
- * Result of resolving maven artifact dependencies.
+ * Output of {@link Call}.
+ *
+ * @param <T> type of the body.
*/
-public class ResolveResult {
- /** List of resolver artifacts' paths. */
- private final List<Path> artifacts;
+public interface CallOutput<T> {
+ /**
+ * Body provider method.
+ *
+ * @return Body of the call's output. Can be {@link String} or any other type.
+ */
+ T body();
+
+ /**
+ * Error check method.
+ *
+ * @return True if output has an error.
+ */
+ boolean hasError();
/**
- * Creates result of artifacts resolving.
+ * Check if Call output is empty.
*
- * @param artifacts List of artifacts paths.
+ * @return true if call output is empty.
*/
- public ResolveResult(List<Path> artifacts) {
- this.artifacts = artifacts;
- }
+ boolean isEmpty();
/**
- * Returns list of artifacts' paths.
+ * Exception cause provider method.
*
- * @return List of artifacts' paths.
+ * @return the cause of the error.
*/
- public List<Path> artifacts() {
- return Collections.unmodifiableList(artifacts);
- }
+ Throwable errorCause();
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallOutputStatus.java
similarity index 86%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallOutputStatus.java
index 5cb93f287..8d9b182ab 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallOutputStatus.java
@@ -15,8 +15,11 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.core.call;
+
/**
- * Contains classes for Ignite module management.
+ * The status of the call.
*/
-
-package org.apache.ignite.cli.builtins.module;
+public enum CallOutputStatus {
+ SUCCESS, ERROR, EMPTY
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/call/DefaultCallOutput.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/DefaultCallOutput.java
new file mode 100644
index 000000000..9bbaf3934
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/DefaultCallOutput.java
@@ -0,0 +1,187 @@
+/*
+ * 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.ignite.cli.core.call;
+
+import java.util.Objects;
+
+/**
+ * Default implementation of {@link CallOutput} with {@link T} body.
+ */
+public class DefaultCallOutput<T> implements CallOutput<T> {
+
+ private final CallOutputStatus status;
+
+ private final T body;
+
+ private final Throwable cause;
+
+ private DefaultCallOutput(CallOutputStatus status, T body, Throwable cause) {
+ this.status = status;
+ this.body = body;
+ this.cause = cause;
+ }
+
+ @Override
+ public boolean hasError() {
+ return cause != null;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return CallOutputStatus.EMPTY.equals(status);
+ }
+
+ @Override
+ public Throwable errorCause() {
+ return cause;
+ }
+
+ @Override
+ public T body() {
+ return body;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ DefaultCallOutput<?> that = (DefaultCallOutput<?>) o;
+ return status == that.status && Objects.equals(body, that.body) && Objects.equals(cause, that.cause);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(status, body, cause);
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultCallOutput{"
+ + "status="
+ + status
+ + ", body='"
+ + body + '\''
+ + ", cause="
+ + cause
+ + '}';
+ }
+
+ /**
+ * Builder method provider.
+ *
+ * @return new instance of {@link DefaultCallOutputBuilder}.
+ */
+ public static <T> DefaultCallOutputBuilder<T> builder() {
+ return new DefaultCallOutputBuilder<T>();
+ }
+
+ /**
+ * New successful call output with provided body.
+ *
+ * @param body for successful call output.
+ * @return Successful call output with provided body.
+ */
+ public static <T> DefaultCallOutput<T> success(T body) {
+ return DefaultCallOutput.<T>builder()
+ .status(CallOutputStatus.SUCCESS)
+ .body(body)
+ .build();
+ }
+
+ /**
+ * New failed call output with provided cause.
+ *
+ * @param cause error of failed call.
+ * @return Failed call output with provided cause.
+ */
+ public static <T> DefaultCallOutput<T> failure(Throwable cause) {
+ return DefaultCallOutput.<T>builder()
+ .status(CallOutputStatus.ERROR)
+ .cause(cause)
+ .build();
+ }
+
+ /**
+ * New empty coll output.
+ *
+ * @return Empty call output.
+ */
+ public static <T> DefaultCallOutput<T> empty() {
+ return DefaultCallOutput.<T>builder()
+ .status(CallOutputStatus.EMPTY)
+ .build();
+ }
+
+ /**
+ * Builder of {@link DefaultCallOutput}.
+ */
+ public static class DefaultCallOutputBuilder<T> {
+
+ private CallOutputStatus status;
+
+ private T body;
+
+ private Throwable cause;
+
+ /**
+ * Builder setter.
+ *
+ * @param status output status.
+ * @return invoked builder instance {@link DefaultCallOutputBuilder}.
+ */
+ public DefaultCallOutputBuilder<T> status(CallOutputStatus status) {
+ this.status = status;
+ return this;
+ }
+
+ /**
+ * Builder setter.
+ *
+ * @param body call output body.
+ * @return invoked builder instance {@link DefaultCallOutputBuilder}.
+ */
+ public DefaultCallOutputBuilder<T> body(T body) {
+ this.body = body;
+ return this;
+ }
+
+ /**
+ * Builder setter.
+ *
+ * @param cause exception cause.
+ * @return invoked builder instance {@link DefaultCallOutputBuilder}.
+ */
+ public DefaultCallOutputBuilder<T> cause(Throwable cause) {
+ this.cause = cause;
+ return this;
+ }
+
+ /**
+ * Build method.
+ *
+ * @return new {@link DefaultCallOutput} with field provided to builder.
+ */
+ public DefaultCallOutput<T> build() {
+ return new DefaultCallOutput<>(status, body, cause);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/EmptyCallInput.java
similarity index 84%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/call/EmptyCallInput.java
index 5cb93f287..3e866543e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/EmptyCallInput.java
@@ -15,8 +15,10 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.core.call;
+
/**
- * Contains classes for Ignite module management.
+ * Input for executing commands without arguments.
*/
-
-package org.apache.ignite.cli.builtins.module;
+public class EmptyCallInput implements CallInput {
+}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/StatusCallInput.java
similarity index 72%
copy from modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/call/StatusCallInput.java
index 7387f57aa..74aab7f89 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/StatusCallInput.java
@@ -15,19 +15,19 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
-
-import org.junit.jupiter.api.BeforeAll;
+package org.apache.ignite.cli.core.call;
/**
- * Base class for any CLI tests.
+ * Input for status call.
*/
-public abstract class AbstractCliTest {
- /**
- * Sets up a dumb terminal before tests.
- */
- @BeforeAll
- static void beforeAll() {
- System.setProperty("org.jline.terminal.dumb", "true");
+public class StatusCallInput implements CallInput {
+ private final String clusterUrl;
+
+ public StatusCallInput(String clusterUrl) {
+ this.clusterUrl = clusterUrl;
+ }
+
+ public String getClusterUrl() {
+ return clusterUrl;
}
}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/StringCallInput.java
similarity index 67%
copy from modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/call/StringCallInput.java
index 7387f57aa..c54f73a4b 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/StringCallInput.java
@@ -15,19 +15,24 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
-
-import org.junit.jupiter.api.BeforeAll;
+package org.apache.ignite.cli.core.call;
/**
- * Base class for any CLI tests.
+ * Input for executing commands with {@code String} arguments.
*/
-public abstract class AbstractCliTest {
+public class StringCallInput implements CallInput {
+ private final String string;
+
+ public StringCallInput(String string) {
+ this.string = string;
+ }
+
/**
- * Sets up a dumb terminal before tests.
+ * Argument getter.
+ *
+ * @return {@code String} argument.
*/
- @BeforeAll
- static void beforeAll() {
- System.setProperty("org.jline.terminal.dumb", "true");
+ public String getString() {
+ return string;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ResolveResult.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/CommandExecutionException.java
similarity index 55%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ResolveResult.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/CommandExecutionException.java
index 658f069a9..a15f76abf 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ResolveResult.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/CommandExecutionException.java
@@ -15,34 +15,42 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.builtins.module;
-
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
+package org.apache.ignite.cli.core.exception;
/**
- * Result of resolving maven artifact dependencies.
+ * Command execution exception.
*/
-public class ResolveResult {
- /** List of resolver artifacts' paths. */
- private final List<Path> artifacts;
+public class CommandExecutionException extends RuntimeException {
+
+ private final String commandId;
+
+ private final String reason;
+
+ public CommandExecutionException(String commandId, String reason) {
+ this.commandId = commandId;
+ this.reason = reason;
+ }
/**
- * Creates result of artifacts resolving.
+ * Command identifier getter.
*
- * @param artifacts List of artifacts paths.
+ * @return command identifier.
*/
- public ResolveResult(List<Path> artifacts) {
- this.artifacts = artifacts;
+ public String getCommandId() {
+ return commandId;
}
/**
- * Returns list of artifacts' paths.
+ * Exception reason getter.
*
- * @return List of artifacts' paths.
+ * @return exception reason.
*/
- public List<Path> artifacts() {
- return Collections.unmodifiableList(artifacts);
+ public String getReason() {
+ return reason;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ConnectCommandException.java
similarity index 64%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ConnectCommandException.java
index 32fbdbfdb..140ddbe1a 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ConnectCommandException.java
@@ -15,22 +15,29 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
-
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+package org.apache.ignite.cli.core.exception;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Connect command exception.
*/
-public abstract class NoOpHandler extends Handler {
- @Override
- public void flush() {
- // no-op
+public class ConnectCommandException extends RuntimeException {
+ private final String reason;
+
+ public ConnectCommandException(String reason) {
+ this.reason = reason;
+ }
+
+ /**
+ * Exception reason getter.
+ *
+ * @return exception reason.
+ */
+ public String getReason() {
+ return reason;
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public String toString() {
+ return super.toString();
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandler.java
new file mode 100644
index 000000000..e16798b69
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandler.java
@@ -0,0 +1,58 @@
+/*
+ * 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.ignite.cli.core.exception;
+
+
+import org.apache.ignite.lang.IgniteLogger;
+
+/**
+ * General interface of exception handler.
+ *
+ * @param <T> exception type.
+ */
+public interface ExceptionHandler<T extends Throwable> {
+ IgniteLogger logger = IgniteLogger.forClass(ExceptionHandler.class);
+
+ ExceptionHandler<Throwable> DEFAULT = new ExceptionHandler<>() {
+ @Override
+ public void handle(ExceptionWriter err, Throwable e) {
+ logger.error("Unhandled exception ", e);
+ err.write("Internal error!");
+ }
+
+ @Override
+ public Class<Throwable> applicableException() {
+ return Throwable.class;
+ }
+ };
+
+ /**
+ * Handler method.
+ *
+ * @param err writer instance for any error messages.
+ * @param e exception instance.
+ */
+ void handle(ExceptionWriter err, T e);
+
+ /**
+ * Exception class getter.
+ *
+ * @return class of applicable exception for the handler.
+ */
+ Class<T> applicableException();
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandlers.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandlers.java
new file mode 100644
index 000000000..09437e679
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandlers.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ignite.cli.core.exception;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Exception handlers collector class.
+ */
+public class ExceptionHandlers {
+ private final Map<Class<? extends Throwable>, ExceptionHandler<? extends Throwable>> map = new HashMap<>();
+ private final ExceptionHandler<Throwable> defaultHandler;
+
+ public ExceptionHandlers() {
+ this(ExceptionHandler.DEFAULT);
+ }
+
+ public ExceptionHandlers(ExceptionHandler<Throwable> defaultHandler) {
+ this.defaultHandler = defaultHandler;
+ }
+
+ /**
+ * Appends exception handler to collection.
+ * If collection already contains handler for type {@param <T>} it will be replaced by {@param exceptionHandler}.
+ *
+ * @param exceptionHandler handler instance.
+ * @param <T> exception type.
+ */
+ public <T extends Throwable> void addExceptionHandler(ExceptionHandler<T> exceptionHandler) {
+ map.put(exceptionHandler.applicableException(), exceptionHandler);
+ }
+
+ /**
+ * Append all exception handlers.
+ *
+ * @param exceptionHandlers handlers to append.
+ */
+ public void addExceptionHandlers(ExceptionHandlers exceptionHandlers) {
+ map.putAll(exceptionHandlers.map);
+ }
+
+ /**
+ * Handle method.
+ *
+ * @param errOutput error output.
+ * @param e exception instance.
+ * @param <T> exception type.
+ */
+ public <T extends Throwable> void handleException(ExceptionWriter errOutput, T e) {
+ processException(errOutput, e instanceof WrappedException ? e.getCause() : e);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T extends Throwable> void processException(ExceptionWriter errOutput, T e) {
+ ExceptionHandler<T> exceptionHandler = (ExceptionHandler<T>) map.get(e.getClass());
+ if (exceptionHandler != null) {
+ exceptionHandler.handle(errOutput, e);
+ } else {
+ defaultHandler.handle(errOutput, e);
+ }
+ }
+
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionWriter.java
similarity index 62%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionWriter.java
index 178725423..ec85568bf 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionWriter.java
@@ -15,27 +15,28 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.builtins.config;
+package org.apache.ignite.cli.core.exception;
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-import java.net.http.HttpClient;
+import java.io.PrintWriter;
/**
- * Factory for producing simple HTTP clients.
+ * Writer for exception error messages.
*/
-@Factory
-public class HttpClientFactory {
+public interface ExceptionWriter {
/**
- * Creates new HTTP client.
+ * Write provided exception message.
*
- * @return HttpClient
+ * @param errMessage error message.
*/
- @Singleton
- HttpClient httpClient() {
- return HttpClient
- .newBuilder()
- .version(HttpClient.Version.HTTP_1_1)
- .build();
+ void write(String errMessage);
+
+ /**
+ * Helper mapper.
+ *
+ * @param pw {@link PrintWriter} instance.
+ * @return {@link ExceptionWriter} instance based on {@param pw}.
+ */
+ static ExceptionWriter fromPrintWriter(PrintWriter pw) {
+ return pw::println;
}
}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/WrappedException.java
similarity index 72%
copy from modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/WrappedException.java
index 7387f57aa..0fd46f1a0 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/AbstractCliTest.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/WrappedException.java
@@ -15,19 +15,19 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
-
-import org.junit.jupiter.api.BeforeAll;
+package org.apache.ignite.cli.core.exception;
/**
- * Base class for any CLI tests.
+ * Wrapper for checked exception.
+ * This exception will be handled as cause type.
*/
-public abstract class AbstractCliTest {
+public class WrappedException extends RuntimeException {
/**
- * Sets up a dumb terminal before tests.
+ * Constructor.
+ *
+ * @param cause cause exception.
*/
- @BeforeAll
- static void beforeAll() {
- System.setProperty("org.jline.terminal.dumb", "true");
+ public WrappedException(Throwable cause) {
+ super(cause);
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ApiExceptionHandler.java
similarity index 60%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ApiExceptionHandler.java
index 32fbdbfdb..f581f579a 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ApiExceptionHandler.java
@@ -15,22 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.rest.client.invoker.ApiException;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link ApiException}.
*/
-public abstract class NoOpHandler extends Handler {
+public class ApiExceptionHandler implements ExceptionHandler<ApiException> {
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, ApiException e) {
+ err.write("Api error: " + e.getCause());
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<ApiException> applicableException() {
+ return ApiException.class;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/CommandExecutionExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/CommandExecutionExceptionHandler.java
new file mode 100644
index 000000000..36240644f
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/CommandExecutionExceptionHandler.java
@@ -0,0 +1,41 @@
+/*
+ * 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.ignite.cli.core.exception.handler;
+
+import org.apache.ignite.cli.core.exception.CommandExecutionException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.lang.IgniteLogger;
+
+/**
+ * Exception handler for {@link CommandExecutionException}.
+ */
+public class CommandExecutionExceptionHandler implements ExceptionHandler<CommandExecutionException> {
+ private static final IgniteLogger log = IgniteLogger.forClass(CommandExecutionExceptionHandler.class);
+
+ @Override
+ public void handle(ExceptionWriter err, CommandExecutionException e) {
+ log.error("Command {} failed with reason {}", e.getCommandId(), e.getReason(), e);
+ err.write(String.format("Command %s failed with reason: %s", e.getCommandId(), e.getReason()));
+ }
+
+ @Override
+ public Class<CommandExecutionException> applicableException() {
+ return CommandExecutionException.class;
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConnectCommandExceptionHandler.java
similarity index 57%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConnectCommandExceptionHandler.java
index 32fbdbfdb..2519fda17 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConnectCommandExceptionHandler.java
@@ -15,22 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.core.exception.ConnectCommandException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link ConnectCommandException}.
*/
-public abstract class NoOpHandler extends Handler {
+public class ConnectCommandExceptionHandler implements ExceptionHandler<ConnectCommandException> {
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, ConnectCommandException e) {
+ err.write(e.getReason());
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<ConnectCommandException> applicableException() {
+ return ConnectCommandException.class;
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConnectExceptionHandler.java
similarity index 60%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConnectExceptionHandler.java
index 32fbdbfdb..516e2e6a9 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConnectExceptionHandler.java
@@ -15,22 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import java.net.ConnectException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link ConnectException}.
*/
-public abstract class NoOpHandler extends Handler {
+public class ConnectExceptionHandler implements ExceptionHandler<ConnectException> {
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, ConnectException e) {
+ err.write("Connection failed " + e.getMessage());
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<ConnectException> applicableException() {
+ return ConnectException.class;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
new file mode 100644
index 000000000..175fb7e71
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/DefaultExceptionHandlers.java
@@ -0,0 +1,41 @@
+/*
+ * 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.ignite.cli.core.exception.handler;
+
+import org.apache.ignite.cli.core.exception.ExceptionHandlers;
+
+/**
+ * Default collection of exception handlers.
+ */
+public class DefaultExceptionHandlers extends ExceptionHandlers {
+
+ /**
+ * Constructor.
+ */
+ public DefaultExceptionHandlers() {
+ addExceptionHandler(new SqlExceptionHandler());
+ addExceptionHandler(new ConnectCommandExceptionHandler());
+ addExceptionHandler(new CommandExecutionExceptionHandler());
+ addExceptionHandler(new TimeoutExceptionHandler());
+ addExceptionHandler(new IgniteClientExceptionHandler());
+ addExceptionHandler(new IgniteCliExceptionHandler());
+ addExceptionHandler(new ConnectExceptionHandler());
+ addExceptionHandler(new ApiExceptionHandler());
+ addExceptionHandler(new UnknownCommandExceptionHandler());
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/EndOfFileExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/EndOfFileExceptionHandler.java
new file mode 100644
index 000000000..64d1f85ff
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/EndOfFileExceptionHandler.java
@@ -0,0 +1,55 @@
+/*
+ * 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.ignite.cli.core.exception.handler;
+
+import java.util.function.Consumer;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.jline.reader.EndOfFileException;
+import org.jline.reader.LineReader;
+
+/**
+ * Exception handler for {@link EndOfFileException}.
+ * From {@link EndOfFileException}
+ * <p>
+ * This exception is thrown by {@link LineReader#readLine} when the user types ctrl-D)
+ * </p>
+ * This handler call {@param endAction} to stop some process.
+ */
+public class EndOfFileExceptionHandler implements ExceptionHandler<EndOfFileException> {
+ private final Consumer<Boolean> endAction;
+
+ /**
+ * Constructor.
+ *
+ * @param endAction handle action.
+ */
+ public EndOfFileExceptionHandler(Consumer<Boolean> endAction) {
+ this.endAction = endAction;
+ }
+
+ @Override
+ public void handle(ExceptionWriter err, EndOfFileException e) {
+ endAction.accept(true);
+ }
+
+ @Override
+ public Class<EndOfFileException> applicableException() {
+ return EndOfFileException.class;
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
similarity index 59%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
index 32fbdbfdb..03cf5a72b 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
@@ -15,22 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.deprecated.IgniteCliException;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link IgniteCliException}.
*/
-public abstract class NoOpHandler extends Handler {
+public class IgniteCliExceptionHandler implements ExceptionHandler<IgniteCliException> {
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, IgniteCliException e) {
+ err.write(e.getMessage());
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<IgniteCliException> applicableException() {
+ return IgniteCliException.class;
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteClientExceptionHandler.java
similarity index 50%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteClientExceptionHandler.java
index 32fbdbfdb..765d3e280 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteClientExceptionHandler.java
@@ -15,22 +15,27 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.client.IgniteClientException;
+import org.apache.ignite.lang.IgniteLogger;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link IgniteClientException}.
*/
-public abstract class NoOpHandler extends Handler {
+public class IgniteClientExceptionHandler implements ExceptionHandler<IgniteClientException> {
+ private static final IgniteLogger log = IgniteLogger.forClass(IgniteClientExceptionHandler.class);
+
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, IgniteClientException e) {
+ log.error("Ignite client exception", e);
+ err.write("Ignite client exception with code: " + e.errorCode());
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<IgniteClientException> applicableException() {
+ return IgniteClientException.class;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/PicocliExecutionExceptionHandler.java
similarity index 54%
copy from modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/PicocliExecutionExceptionHandler.java
index 2e0050be3..939db3c14 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/CommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/PicocliExecutionExceptionHandler.java
@@ -15,15 +15,22 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.spec;
+package org.apache.ignite.cli.core.exception.handler;
+import org.apache.ignite.cli.core.exception.ExceptionHandlers;
import picocli.CommandLine;
+import picocli.CommandLine.IExecutionExceptionHandler;
+import picocli.CommandLine.ParseResult;
/**
- * Base class for commands without any subcommands.
+ * Implementation of {@link IExecutionExceptionHandler} based on {@link ExceptionHandlers}.
*/
-public abstract class CommandSpec extends SpecAdapter {
- /** Help option specification. */
- @CommandLine.Option(names = "--help", usageHelp = true, hidden = true)
- protected boolean usageHelpRequested;
+public class PicocliExecutionExceptionHandler implements IExecutionExceptionHandler {
+ private final ExceptionHandlers exceptionHandlers = new DefaultExceptionHandlers();
+
+ @Override
+ public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) {
+ exceptionHandlers.handleException(System.err::println, ex);
+ return 1;
+ }
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ReplExceptionHandlers.java
similarity index 62%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ReplExceptionHandlers.java
index 178725423..dbae73a2b 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/config/HttpClientFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ReplExceptionHandlers.java
@@ -15,27 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.builtins.config;
+package org.apache.ignite.cli.core.exception.handler;
-import io.micronaut.context.annotation.Factory;
-import jakarta.inject.Singleton;
-import java.net.http.HttpClient;
+import java.util.function.Consumer;
+import org.apache.ignite.cli.core.exception.ExceptionHandlers;
/**
- * Factory for producing simple HTTP clients.
+ * Collection of exception handlers for REPL.
*/
-@Factory
-public class HttpClientFactory {
+public class ReplExceptionHandlers extends ExceptionHandlers {
+
/**
- * Creates new HTTP client.
+ * Constructor.
*
- * @return HttpClient
+ * @param stop REPL stop action.
*/
- @Singleton
- HttpClient httpClient() {
- return HttpClient
- .newBuilder()
- .version(HttpClient.Version.HTTP_1_1)
- .build();
+ public ReplExceptionHandlers(Consumer<Boolean> stop) {
+ addExceptionHandler(new EndOfFileExceptionHandler(stop));
+ addExceptionHandler(new UserInterruptExceptionHandler());
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SqlExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SqlExceptionHandler.java
new file mode 100644
index 000000000..3f60ad941
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SqlExceptionHandler.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.ignite.cli.core.exception.handler;
+
+import java.sql.SQLException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
+import org.apache.ignite.lang.IgniteLogger;
+
+/**
+ * Exception handler for {@link SQLException}.
+ */
+public class SqlExceptionHandler implements ExceptionHandler<SQLException> {
+ private static final IgniteLogger log = IgniteLogger.forClass(SqlExceptionHandler.class);
+
+ public static final String PARSING_ERROR_MESSAGE = "SQL query parsing error: %s";
+
+ public static final String INVALID_PARAMETER_MESSAGE = "Invalid parameter value.";
+
+ public static final String CLIENT_CONNECTION_FAILED_MESSAGE = "Connection failed.";
+
+ public static final String CONNECTION_BROKE_MESSAGE = "Connection error.";
+
+ @Override
+ public void handle(ExceptionWriter err, SQLException e) {
+ switch (e.getSQLState()) {
+ case SqlStateCode.CONNECTION_FAILURE:
+ case SqlStateCode.CONNECTION_CLOSED:
+ case SqlStateCode.CONNECTION_REJECTED:
+ err.write(CONNECTION_BROKE_MESSAGE);
+ break;
+ case SqlStateCode.PARSING_EXCEPTION:
+ err.write(String.format(PARSING_ERROR_MESSAGE, e.getMessage()));
+ break;
+ case SqlStateCode.INVALID_PARAMETER_VALUE:
+ err.write(INVALID_PARAMETER_MESSAGE);
+ break;
+ case SqlStateCode.CLIENT_CONNECTION_FAILED:
+ err.write(CLIENT_CONNECTION_FAILED_MESSAGE);
+ break;
+ default:
+ log.error("Unrecognized error ", e);
+ err.write("Unrecognized error while process SQL query.");
+ }
+ }
+
+ @Override
+ public Class<SQLException> applicableException() {
+ return SQLException.class;
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/TimeoutExceptionHandler.java
similarity index 52%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/TimeoutExceptionHandler.java
index 32fbdbfdb..86a6881ba 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/TimeoutExceptionHandler.java
@@ -15,22 +15,27 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import java.util.concurrent.TimeoutException;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.lang.IgniteLogger;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link TimeoutException}.
*/
-public abstract class NoOpHandler extends Handler {
+public class TimeoutExceptionHandler implements ExceptionHandler<TimeoutException> {
+ private static final IgniteLogger log = IgniteLogger.forClass(TimeoutExceptionHandler.class);
+
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, TimeoutException e) {
+ log.error("Timeout exception ", e);
+ err.write("Command failed with timeout.");
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<TimeoutException> applicableException() {
+ return TimeoutException.class;
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UnknownCommandExceptionHandler.java
similarity index 52%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UnknownCommandExceptionHandler.java
index 32fbdbfdb..989d05055 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UnknownCommandExceptionHandler.java
@@ -15,22 +15,25 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.jline.console.impl.SystemRegistryImpl.UnknownCommandException;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link UnknownCommandException}.
+ * This exception is thrown by {@link org.jline.console.SystemRegistry#execute(String)} when the user types invalid or unknown command.
*/
-public abstract class NoOpHandler extends Handler {
+public class UnknownCommandExceptionHandler implements ExceptionHandler<UnknownCommandException> {
+
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, UnknownCommandException e) {
+ err.write(e.getMessage());
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<UnknownCommandException> applicableException() {
+ return UnknownCommandException.class;
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UserInterruptExceptionHandler.java
similarity index 59%
copy from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UserInterruptExceptionHandler.java
index 32fbdbfdb..46da1ee2e 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UserInterruptExceptionHandler.java
@@ -15,22 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.exception.handler;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.apache.ignite.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.jline.reader.UserInterruptException;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Exception handler for {@link UserInterruptException}.
*/
-public abstract class NoOpHandler extends Handler {
+public class UserInterruptExceptionHandler implements ExceptionHandler<UserInterruptException> {
@Override
- public void flush() {
- // no-op
+ public void handle(ExceptionWriter err, UserInterruptException e) {
+ //NOOP
}
@Override
- public void close() throws SecurityException {
- // no-op
+ public Class<UserInterruptException> applicableException() {
+ return UserInterruptException.class;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/Repl.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/Repl.java
new file mode 100644
index 000000000..78e0f7fd5
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/Repl.java
@@ -0,0 +1,150 @@
+/*
+ * 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.ignite.cli.core.repl;
+
+import java.util.Map;
+import org.apache.ignite.cli.core.CallExecutionPipelineProvider;
+import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.exception.ExceptionHandlers;
+import org.apache.ignite.cli.core.repl.executor.RegistryCommandExecutor;
+import org.apache.ignite.cli.core.repl.prompt.PromptProvider;
+import org.apache.ignite.cli.core.repl.terminal.TerminalCustomizer;
+import org.jline.reader.Completer;
+import org.jline.terminal.Terminal;
+import picocli.CommandLine.IDefaultValueProvider;
+
+/**
+ * Data class with all information about REPL.
+ */
+public class Repl {
+
+ private final PromptProvider promptProvider;
+
+ private final Map<String, String> aliases;
+
+ private final TerminalCustomizer terminalCustomizer;
+
+ private final Class<?> commandClass;
+
+ private final IDefaultValueProvider defaultValueProvider;
+
+ private final CallExecutionPipelineProvider provider;
+
+ private final Completer completer;
+
+ private final String historyFileName;
+
+ private final boolean tailTipWidgetsEnabled;
+
+ /**
+ * Constructor.
+ *
+ * @param promptProvider REPL prompt provider.
+ * @param commandClass top level command class.
+ * @param defaultValueProvider default value provider.
+ * @param aliases map of aliases for commands.
+ * @param terminalCustomizer customizer of terminal.
+ * @param provider default call execution pipeline provider.
+ * @param completer completer instance.
+ * @param historyFileName file name for storing commands history.
+ * @param tailTipWidgetsEnabled whether tailtip widgets are enabled.
+ */
+ public Repl(PromptProvider promptProvider,
+ Class<?> commandClass,
+ IDefaultValueProvider defaultValueProvider,
+ Map<String, String> aliases,
+ TerminalCustomizer terminalCustomizer,
+ CallExecutionPipelineProvider provider,
+ Completer completer,
+ String historyFileName,
+ boolean tailTipWidgetsEnabled
+ ) {
+ this.promptProvider = promptProvider;
+ this.commandClass = commandClass;
+ this.defaultValueProvider = defaultValueProvider;
+ this.aliases = aliases;
+ this.terminalCustomizer = terminalCustomizer;
+ this.provider = provider;
+ this.completer = completer;
+ this.historyFileName = historyFileName;
+ this.tailTipWidgetsEnabled = tailTipWidgetsEnabled;
+ }
+
+ /**
+ * Builder provider method.
+ *
+ * @return new instance of builder {@link ReplBuilder}.
+ */
+ public static ReplBuilder builder() {
+ return new ReplBuilder();
+ }
+
+ public PromptProvider getPromptProvider() {
+ return promptProvider;
+ }
+
+ /**
+ * Getter for {@code commandClass} field.
+ *
+ * @return class with top level command.
+ */
+ public Class<?> commandClass() {
+ return commandClass;
+ }
+
+ /**
+ * Getter for {@code defaultValueProvider} field.
+ *
+ * @return default value provider.
+ */
+ public IDefaultValueProvider defaultValueProvider() {
+ return defaultValueProvider;
+ }
+
+ /**
+ * Getter for {@code aliases} field.
+ *
+ * @return map of command aliases.
+ */
+ public Map<String, String> getAliases() {
+ return aliases;
+ }
+
+ /**
+ * Method for {@param terminal} customization.
+ */
+ public void customizeTerminal(Terminal terminal) {
+ terminalCustomizer.customize(terminal);
+ }
+
+ public CallExecutionPipeline<?, ?> getPipeline(RegistryCommandExecutor executor, ExceptionHandlers exceptionHandlers, String line) {
+ return provider.get(executor, exceptionHandlers, line);
+ }
+
+ public Completer getCompleter() {
+ return completer;
+ }
+
+ public String getHistoryFileName() {
+ return historyFileName;
+ }
+
+ public boolean isTailTipWidgetsEnabled() {
+ return tailTipWidgetsEnabled;
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/ReplBuilder.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/ReplBuilder.java
new file mode 100644
index 000000000..838a8f076
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/ReplBuilder.java
@@ -0,0 +1,139 @@
+/*
+ * 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.ignite.cli.core.repl;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.ignite.cli.core.CallExecutionPipelineProvider;
+import org.apache.ignite.cli.core.repl.prompt.PromptProvider;
+import org.apache.ignite.cli.core.repl.terminal.TerminalCustomizer;
+import org.jline.reader.Completer;
+import picocli.CommandLine.IDefaultValueProvider;
+
+/**
+ * Builder of {@link Repl}.
+ */
+public class ReplBuilder {
+
+ private PromptProvider promptProvider;
+
+ private final Map<String, String> aliases = new HashMap<>();
+
+ private Class<?> commandClass;
+
+ private IDefaultValueProvider defaultValueProvider;
+
+ private TerminalCustomizer terminalCustomizer = terminal -> {
+ };
+
+ private Completer completer;
+
+ private CallExecutionPipelineProvider provider;
+
+ private String historyFileName;
+
+ private boolean tailTipWidgetsEnabled;
+
+ /**
+ * Build methods.
+ *
+ * @return new instance of {@link Repl}.
+ */
+ public Repl build() {
+ return new Repl(
+ promptProvider,
+ commandClass,
+ defaultValueProvider,
+ aliases,
+ terminalCustomizer,
+ provider,
+ completer,
+ historyFileName,
+ tailTipWidgetsEnabled
+ );
+ }
+
+ public ReplBuilder withPromptProvider(PromptProvider promptProvider) {
+ this.promptProvider = promptProvider;
+ return this;
+ }
+
+ /**
+ * Builder setter of {@code commandClass} field.
+ *
+ * @param commandClass class with top level command.
+ * @return invoked builder instance {@link ReplBuilder}.
+ */
+ public ReplBuilder withCommandClass(Class<?> commandClass) {
+ this.commandClass = commandClass;
+ return this;
+ }
+
+ /**
+ * Builder setter of {@code defaultValueProvider} field.
+ *
+ * @param defaultValueProvider default value provider.
+ * @return invoked builder instance {@link ReplBuilder}.
+ */
+ public ReplBuilder withDefaultValueProvider(IDefaultValueProvider defaultValueProvider) {
+ this.defaultValueProvider = defaultValueProvider;
+ return this;
+ }
+
+ /**
+ * Builder setter of {@code aliases} field.
+ *
+ * @param aliases map of aliases for commands.
+ * @return invoked builder instance {@link ReplBuilder}.
+ */
+ public ReplBuilder withAliases(Map<String, String> aliases) {
+ this.aliases.putAll(aliases);
+ return this;
+ }
+
+ /**
+ * Builder setter of {@code terminalCustomizer} field.
+ *
+ * @param terminalCustomizer customizer of terminal {@link org.jline.terminal.Terminal}.
+ * @return invoked builder instance {@link ReplBuilder}.
+ */
+ public ReplBuilder withTerminalCustomizer(TerminalCustomizer terminalCustomizer) {
+ this.terminalCustomizer = terminalCustomizer;
+ return this;
+ }
+
+ public ReplBuilder withCompleter(Completer completer) {
+ this.completer = completer;
+ return this;
+ }
+
+ public ReplBuilder withCallExecutionPipelineProvider(CallExecutionPipelineProvider provider) {
+ this.provider = provider;
+ return this;
+ }
+
+ public ReplBuilder withHistoryFileName(String historyFileName) {
+ this.historyFileName = historyFileName;
+ return this;
+ }
+
+ public ReplBuilder withTailTipWidgets() {
+ this.tailTipWidgetsEnabled = true;
+ return this;
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/Session.java
similarity index 53%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/repl/Session.java
index 9099a22db..c42147a46 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/Session.java
@@ -15,36 +15,44 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.repl;
-import io.micronaut.core.annotation.Introspected;
-import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import picocli.CommandLine;
/**
- * Version provider for Picocli interactions.
+ * Connection session that in fact is holder for state: connected or disconnected.
+ * Also has a nodeUrl if the state is connected.
*/
@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
-
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
- @Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
+public class Session {
+
+ private boolean connectedToNode;
+
+ private String nodeUrl;
+
+ private String jdbcUrl;
+
+ public boolean isConnectedToNode() {
+ return connectedToNode;
+ }
+
+ public void setConnectedToNode(boolean connectedToNode) {
+ this.connectedToNode = connectedToNode;
+ }
+
+ public String getNodeUrl() {
+ return nodeUrl;
+ }
+
+ public void setNodeUrl(String nodeUrl) {
+ this.nodeUrl = nodeUrl;
+ }
+
+ public String getJdbcUrl() {
+ return jdbcUrl;
}
- /** {@inheritDoc} */
- @Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public void setJdbcUrl(String jdbcUrl) {
+ this.jdbcUrl = jdbcUrl;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/SessionDefaultValueProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/SessionDefaultValueProvider.java
new file mode 100644
index 000000000..68541cfd3
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/SessionDefaultValueProvider.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ignite.cli.core.repl;
+
+import jakarta.inject.Singleton;
+import java.util.Objects;
+import org.apache.ignite.cli.config.Config;
+import picocli.CommandLine.IDefaultValueProvider;
+import picocli.CommandLine.Model.ArgSpec;
+import picocli.CommandLine.PropertiesDefaultProvider;
+
+/**
+ * Implementation of {@link IDefaultValueProvider} based on {@link Session}.
+ */
+@Singleton
+public class SessionDefaultValueProvider implements IDefaultValueProvider {
+
+ private final Session session;
+
+ private final IDefaultValueProvider defaultValueProvider;
+
+ /**
+ * Constructor.
+ *
+ * @param session session instance.
+ * @param config config instance.
+ */
+ public SessionDefaultValueProvider(Session session, Config config) {
+ this.session = session;
+ this.defaultValueProvider = new PropertiesDefaultProvider(config.getProperties());
+ }
+
+ @Override
+ public String defaultValue(ArgSpec argSpec) throws Exception {
+ if (session.isConnectedToNode()) {
+ if (Objects.equals(argSpec.descriptionKey(), "ignite.jdbc-url")) {
+ return session.getJdbcUrl();
+ }
+ }
+ return defaultValueProvider.defaultValue(argSpec);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/config/ClientConnectorConfig.java
similarity index 73%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/repl/config/ClientConnectorConfig.java
index 5cb93f287..60d92eada 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/config/ClientConnectorConfig.java
@@ -15,8 +15,16 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.core.repl.config;
+
+import org.apache.ignite.client.IgniteClientConfiguration;
+
/**
- * Contains classes for Ignite module management.
+ * DTO class for client connector config.
*/
-
-package org.apache.ignite.cli.builtins.module;
+public class ClientConnectorConfig {
+ /**
+ * Ignite client port.
+ */
+ public int port = IgniteClientConfiguration.DFLT_PORT;
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/config/RootConfig.java
similarity index 79%
copy from modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/repl/config/RootConfig.java
index 5cb93f287..8c58aaaf0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/package-info.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/config/RootConfig.java
@@ -15,8 +15,14 @@
* limitations under the License.
*/
+package org.apache.ignite.cli.core.repl.config;
+
/**
- * Contains classes for Ignite module management.
+ * DTO class for node configuration JSON.
*/
-
-package org.apache.ignite.cli.builtins.module;
+public class RootConfig {
+ /**
+ * Client connector part.
+ */
+ public ClientConnectorConfig clientConnector;
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/RegistryCommandExecutor.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/RegistryCommandExecutor.java
new file mode 100644
index 000000000..5077fde67
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/RegistryCommandExecutor.java
@@ -0,0 +1,68 @@
+/*
+ * 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.ignite.cli.core.repl.executor;
+
+
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.call.StringCallInput;
+import org.jline.console.SystemRegistry;
+
+/**
+ * Command executor based on {@link SystemRegistry}.
+ */
+public class RegistryCommandExecutor implements Call<StringCallInput, Object> {
+ private final SystemRegistry systemRegistry;
+
+ /**
+ * Constructor.
+ *
+ * @param systemRegistry {@link SystemRegistry} instance.
+ */
+ public RegistryCommandExecutor(SystemRegistry systemRegistry) {
+ this.systemRegistry = systemRegistry;
+ }
+
+ /**
+ * Executor method.
+ *
+ * @param input processed command line.
+ * @return Command output.
+ */
+ @Override
+ public CallOutput<Object> execute(StringCallInput input) {
+ try {
+ Object executionResult = systemRegistry.execute(input.getString());
+ if (executionResult == null) {
+ return DefaultCallOutput.empty();
+ }
+
+ return DefaultCallOutput.success(executionResult);
+ } catch (Exception e) {
+ return DefaultCallOutput.failure(e);
+ }
+ }
+
+ /**
+ * Clean up {@link SystemRegistry}.
+ */
+ public void cleanUp() {
+ systemRegistry.cleanUp();
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutor.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutor.java
new file mode 100644
index 000000000..4ab85fd74
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutor.java
@@ -0,0 +1,145 @@
+/*
+ * 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.ignite.cli.core.repl.executor;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+import org.apache.ignite.cli.config.Config;
+import org.apache.ignite.cli.core.exception.handler.PicocliExecutionExceptionHandler;
+import org.apache.ignite.cli.core.exception.handler.ReplExceptionHandlers;
+import org.apache.ignite.cli.core.repl.Repl;
+import org.apache.ignite.cli.core.repl.expander.NoopExpander;
+import org.jline.console.impl.SystemRegistryImpl;
+import org.jline.reader.Completer;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReader.SuggestionType;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.reader.MaskingCallback;
+import org.jline.reader.Parser;
+import org.jline.reader.impl.DefaultParser;
+import org.jline.terminal.Terminal;
+import org.jline.widget.TailTipWidgets;
+import picocli.CommandLine;
+import picocli.CommandLine.Help.Ansi;
+import picocli.CommandLine.IDefaultValueProvider;
+import picocli.shell.jline3.PicocliCommands;
+import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
+
+/**
+ * Executor of {@link Repl}.
+ */
+public class ReplExecutor {
+
+ private final Parser parser = new DefaultParser();
+
+ private final Supplier<Path> workDirProvider = () -> Paths.get(System.getProperty("user.dir"));
+
+ private final AtomicBoolean interrupted = new AtomicBoolean();
+
+ private final ReplExceptionHandlers exceptionHandlers = new ReplExceptionHandlers(interrupted::set);
+
+ private final PicocliCommandsFactory factory;
+
+ private final Terminal terminal;
+
+ /**
+ * Constructor.
+ *
+ * @param commandsFactory picocli commands factory.
+ * @param terminal terminal instance.
+ */
+ public ReplExecutor(PicocliCommandsFactory commandsFactory, Terminal terminal) {
+ this.factory = commandsFactory;
+ this.terminal = terminal;
+ }
+
+ /**
+ * Executor method. This is thread blocking method, until REPL stop executing.
+ *
+ * @param repl data class of executing REPL.
+ */
+ public void execute(Repl repl) {
+ try {
+ repl.customizeTerminal(terminal);
+
+ PicocliCommands picocliCommands = createPicocliCommands(repl);
+ SystemRegistryImpl registry = new SystemRegistryImpl(parser, terminal, workDirProvider, null);
+ registry.setCommandRegistries(picocliCommands);
+ registry.register("help", picocliCommands);
+
+ LineReader reader = createReader(repl.getCompleter() != null
+ ? repl.getCompleter()
+ : registry.completer());
+ if (repl.getHistoryFileName() != null) {
+ reader.variable(LineReader.HISTORY_FILE, new File(Config.getStateFolder(), repl.getHistoryFileName()));
+ }
+
+ RegistryCommandExecutor executor = new RegistryCommandExecutor(registry);
+ if (repl.isTailTipWidgetsEnabled()) {
+ TailTipWidgets widgets = new TailTipWidgets(reader, registry::commandDescription, 5,
+ TailTipWidgets.TipType.COMPLETER);
+ widgets.enable();
+ // Workaround for jline issue where TailTipWidgets will produce NPE when passed a bracket
+ registry.setScriptDescription(cmdLine -> null);
+ }
+
+ while (!interrupted.get()) {
+ try {
+ executor.cleanUp();
+ String prompt = Ansi.AUTO.string(repl.getPromptProvider().getPrompt());
+ String line = reader.readLine(prompt, null, (MaskingCallback) null, null);
+ if (line.isEmpty()) {
+ continue;
+ }
+
+ repl.getPipeline(executor, exceptionHandlers, line).runPipeline();
+ } catch (Throwable t) {
+ exceptionHandlers.handleException(System.err::println, t);
+ }
+ }
+ reader.getHistory().save();
+ } catch (Throwable t) {
+ exceptionHandlers.handleException(System.err::println, t);
+ }
+ }
+
+ private LineReader createReader(Completer completer) {
+ LineReader result = LineReaderBuilder.builder()
+ .terminal(terminal)
+ .completer(completer)
+ .parser(parser)
+ .expander(new NoopExpander())
+ .variable(LineReader.LIST_MAX, 50) // max tab completion candidates
+ .build();
+ result.setAutosuggestion(SuggestionType.COMPLETER);
+ return result;
+ }
+
+ private PicocliCommands createPicocliCommands(Repl repl) {
+ CommandLine cmd = new CommandLine(repl.commandClass(), factory);
+ IDefaultValueProvider defaultValueProvider = repl.defaultValueProvider();
+ if (defaultValueProvider != null) {
+ cmd.setDefaultValueProvider(defaultValueProvider);
+ }
+ cmd.setExecutionExceptionHandler(new PicocliExecutionExceptionHandler());
+ return new PicocliCommands(cmd);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutorProvider.java
similarity index 57%
copy from modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutorProvider.java
index 9099a22db..28380dcb0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/VersionProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/ReplExecutorProvider.java
@@ -15,36 +15,30 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.repl.executor;
-import io.micronaut.core.annotation.Introspected;
+import io.micronaut.configuration.picocli.MicronautFactory;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import picocli.CommandLine;
+import org.jline.terminal.Terminal;
+import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
/**
- * Version provider for Picocli interactions.
+ * Provider of {@link ReplExecutor}.
*/
@Singleton
-@Introspected
-public class VersionProvider implements CommandLine.IVersionProvider {
+public class ReplExecutorProvider {
+ private PicocliCommandsFactory factory;
- /** Actual Ignite CLI version info. */
- private final CliVersionInfo cliVerInfo;
-
- /**
- * Creates version provider.
- *
- * @param cliVerInfo Actual Ignite CLI version container.
- */
@Inject
- public VersionProvider(CliVersionInfo cliVerInfo) {
- this.cliVerInfo = cliVerInfo;
+ private Terminal terminal;
+
+ public ReplExecutor get() {
+ return new ReplExecutor(factory, terminal);
}
- /** {@inheritDoc} */
- @Override
- public String[] getVersion() {
- return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+ public void injectFactory(MicronautFactory micronautFactory) {
+ this.factory = new PicocliCommandsFactory(micronautFactory);
+ factory.setTerminal(terminal);
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/SqlQueryCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/SqlQueryCall.java
new file mode 100644
index 000000000..7c19262f1
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/executor/SqlQueryCall.java
@@ -0,0 +1,54 @@
+/*
+ * 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.ignite.cli.core.repl.executor;
+
+import java.sql.SQLException;
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.cli.core.call.StringCallInput;
+import org.apache.ignite.cli.sql.SqlManager;
+import org.apache.ignite.cli.sql.SqlQueryResult;
+
+/**
+ * Call implementation for SQL command execution.
+ */
+public class SqlQueryCall implements Call<StringCallInput, SqlQueryResult> {
+
+ private final SqlManager sqlManager;
+
+ /**
+ * Constructor.
+ *
+ * @param sqlManager SQL manager.
+ */
+ public SqlQueryCall(SqlManager sqlManager) {
+ this.sqlManager = sqlManager;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CallOutput<SqlQueryResult> execute(StringCallInput input) {
+ try {
+ SqlQueryResult result = sqlManager.execute(input.getString());
+ return DefaultCallOutput.success(result);
+ } catch (SQLException e) {
+ return DefaultCallOutput.failure(e);
+ }
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/expander/NoopExpander.java
similarity index 67%
rename from modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/core/repl/expander/NoopExpander.java
index 32fbdbfdb..d6b0893f5 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/NoOpHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/repl/expander/NoopExpander.java
@@ -15,22 +15,24 @@
* limitations under the License.
*/
-package org.apache.ignite.cli;
+package org.apache.ignite.cli.core.repl.expander;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
+import org.jline.reader.Expander;
+import org.jline.reader.History;
/**
- * Adapter for {@link Handler} with empty implementations of all methods but {@link Handler#publish(LogRecord)}.
+ * Noop implementation of {@link Expander}.
*/
-public abstract class NoOpHandler extends Handler {
+public class NoopExpander implements Expander {
+ /** {@inheritDoc} */
@Override
... 4175 lines suppressed ...