This is an automated email from the ASF dual-hosted git repository.

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 0f91ab211ae IGNITE-18945 Add an ability to extend control-utility with 
the new commands - Fixes #10576.
0f91ab211ae is described below

commit 0f91ab211aeb92efcd57aa7586457cfab073d2f7
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Fri Mar 10 14:51:39 2023 +0300

    IGNITE-18945 Add an ability to extend control-utility with the new commands 
- Fixes #10576.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 assembly/dependencies-apache-ignite-lgpl.xml       |   2 +-
 assembly/dependencies-apache-ignite-slim.xml       |   2 +-
 assembly/dependencies-apache-ignite.xml            |   2 +-
 modules/bom/pom.xml                                |   7 +
 modules/control-utility/pom.xml                    |  11 ++
 .../ignite/internal/commandline/Command.java       |  19 +++
 .../internal/commandline/CommandArgIterator.java   |  16 +-
 .../internal/commandline/CommandHandler.java       |  45 +++++-
 .../ignite/internal/commandline/CommandList.java   |  23 ++-
 .../internal/commandline/CommandsProvider.java}    |  22 ++-
 .../internal/commandline/CommonArgParser.java      |  21 ++-
 .../commandline/cache/CacheCommandList.java        | 166 ---------------------
 .../internal/commandline/cache/CacheCommands.java  |   2 +-
 .../commandline/cache/CacheSubcommands.java        |  34 ++---
 .../commandline/CommandHandlerParsingTest.java     |   2 +-
 .../{logo => pluggable}/licenses/apache-2.0.txt    |   0
 modules/extdata/{logo => pluggable}/pom.xml        |  15 +-
 .../commandline/CommandsProviderExtImpl.java       |  90 +++++++++++
 .../commandline/ExtendedControlUtilityTest.java    |  71 +++++++++
 .../ignite/internal/plugin/ExtendedLogoTest.java}  |   4 +-
 .../plugin/IgniteExtLogInfoProviderImpl.java       |   0
 .../IgnitePluggableExtensionsTestSuite.java}       |  10 +-
 ...he.ignite.internal.commandline.CommandsProvider |   1 +
 ...he.ignite.internal.plugin.IgniteLogInfoProvider |   0
 pom.xml                                            |   2 +-
 25 files changed, 330 insertions(+), 237 deletions(-)

diff --git a/assembly/dependencies-apache-ignite-lgpl.xml 
b/assembly/dependencies-apache-ignite-lgpl.xml
index 0e1ec42f4a8..cd273439857 100644
--- a/assembly/dependencies-apache-ignite-lgpl.xml
+++ b/assembly/dependencies-apache-ignite-lgpl.xml
@@ -120,7 +120,7 @@
                 <exclude>${project.groupId}:ignite-extdata-p2p</exclude>
                 <exclude>${project.groupId}:ignite-extdata-uri</exclude>
                 <exclude>${project.groupId}:ignite-extdata-uri-dep</exclude>
-                <exclude>${project.groupId}:ignite-extdata-logo</exclude>
+                <exclude>${project.groupId}:ignite-extdata-pluggable</exclude>
                 <exclude>${project.groupId}:ignite-examples</exclude>
                 <exclude>${project.groupId}:ignite-indexing</exclude>
                 <exclude>${project.groupId}:ignite-codegen</exclude>
diff --git a/assembly/dependencies-apache-ignite-slim.xml 
b/assembly/dependencies-apache-ignite-slim.xml
index eb81a4cdbcb..dbcd1efb2ff 100644
--- a/assembly/dependencies-apache-ignite-slim.xml
+++ b/assembly/dependencies-apache-ignite-slim.xml
@@ -120,7 +120,7 @@
                 <exclude>${project.groupId}:ignite-extdata-p2p</exclude>
                 <exclude>${project.groupId}:ignite-extdata-uri</exclude>
                 <exclude>${project.groupId}:ignite-extdata-uri-dep</exclude>
-                <exclude>${project.groupId}:ignite-extdata-logo</exclude>
+                <exclude>${project.groupId}:ignite-extdata-pluggable</exclude>
                 <exclude>${project.groupId}:ignite-examples</exclude>
                 <exclude>${project.groupId}:ignite-indexing</exclude>
                 <exclude>${project.groupId}:ignite-hadoop</exclude>
diff --git a/assembly/dependencies-apache-ignite.xml 
b/assembly/dependencies-apache-ignite.xml
index 695baee9108..ab81d967cc0 100644
--- a/assembly/dependencies-apache-ignite.xml
+++ b/assembly/dependencies-apache-ignite.xml
@@ -121,7 +121,7 @@
                 <exclude>${project.groupId}:ignite-extdata-p2p</exclude>
                 <exclude>${project.groupId}:ignite-extdata-uri</exclude>
                 <exclude>${project.groupId}:ignite-extdata-uri-dep</exclude>
-                <exclude>${project.groupId}:ignite-extdata-logo</exclude>
+                <exclude>${project.groupId}:ignite-extdata-pluggable</exclude>
                 <exclude>${project.groupId}:ignite-examples</exclude>
                 <exclude>${project.groupId}:ignite-indexing</exclude>
                 <exclude>${project.groupId}:ignite-codegen</exclude>
diff --git a/modules/bom/pom.xml b/modules/bom/pom.xml
index 3ad77023ca2..7c82f120990 100644
--- a/modules/bom/pom.xml
+++ b/modules/bom/pom.xml
@@ -208,6 +208,13 @@
                 <type>test-jar</type>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ignite-control-utility</artifactId>
+                <version>${revision}</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
diff --git a/modules/control-utility/pom.xml b/modules/control-utility/pom.xml
index 42a50502f10..32d50b0ce0e 100644
--- a/modules/control-utility/pom.xml
+++ b/modules/control-utility/pom.xml
@@ -141,6 +141,17 @@
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-deploy-plugin</artifactId>
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/Command.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/Command.java
index 87cc47441d1..b4b9209e637 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/Command.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/Command.java
@@ -124,6 +124,25 @@ public interface Command<T> {
         CommandList cmd,
         @Nullable Map<String, String> paramsDesc,
         String... args
+    ) {
+        usage(logger, desc, cmd.text(), paramsDesc, args);
+    }
+
+    /**
+     * Print command usage.
+     *
+     * @param logger Logger to use.
+     * @param desc Command description.
+     * @param cmd Command.
+     * @param paramsDesc Description of parameters (optional).
+     * @param args Arguments.
+     */
+    public default void usage(
+        IgniteLogger logger,
+        String desc,
+        String cmd,
+        @Nullable Map<String, String> paramsDesc,
+        String... args
     ) {
         logger.info("");
 
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandArgIterator.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandArgIterator.java
index 892f87e0166..3e7d94a2051 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandArgIterator.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandArgIterator.java
@@ -21,6 +21,7 @@ package org.apache.ignite.internal.commandline;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import org.apache.ignite.internal.util.typedef.F;
@@ -31,7 +32,10 @@ import org.jetbrains.annotations.NotNull;
  */
 public class CommandArgIterator {
     /** */
-    private Iterator<String> argsIt;
+    private final Iterator<String> argsIt;
+
+    /** */
+    private final Map<String, Command<?>> cmds;
 
     /** */
     private String peekedArg;
@@ -44,10 +48,16 @@ public class CommandArgIterator {
     /**
      * @param argsIt Raw argument iterator.
      * @param commonArgumentsAndHighLevelCommandSet All known subcomands.
+     * @param cmds Supported commands.
      */
-    public CommandArgIterator(Iterator<String> argsIt, Set<String> 
commonArgumentsAndHighLevelCommandSet) {
+    public CommandArgIterator(
+        Iterator<String> argsIt,
+        Set<String> commonArgumentsAndHighLevelCommandSet,
+        Map<String, Command<?>> cmds
+    ) {
         this.argsIt = argsIt;
         this.commonArgumentsAndHighLevelCommandSet = 
commonArgumentsAndHighLevelCommandSet;
+        this.cmds = cmds;
     }
 
     /**
@@ -61,7 +71,7 @@ public class CommandArgIterator {
      * @return <code>true</code> if there's next argument for subcommand.
      */
     public boolean hasNextSubArg() {
-        return hasNextArg() && CommandList.of(peekNextArg()) == null &&
+        return hasNextArg() && !cmds.containsKey(peekNextArg().toLowerCase()) 
&&
             !commonArgumentsAndHighLevelCommandSet.contains(peekNextArg());
     }
 
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
index c1c4b12d213..967f0114cc5 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
@@ -22,7 +22,9 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Scanner;
 import java.util.UUID;
 import java.util.stream.Collectors;
@@ -112,6 +114,9 @@ public class CommandHandler {
     /** JULs logger. */
     private final IgniteLogger logger;
 
+    /** Supported commands. */
+    private final Map<String, Command<?>> cmds;
+
     /** Session. */
     protected final String ses = U.id8(UUID.randomUUID());
 
@@ -155,7 +160,7 @@ public class CommandHandler {
      *
      */
     public CommandHandler() {
-        logger = setupJavaLogger("control-utility", CommandHandler.class);
+        this(setupJavaLogger("control-utility", CommandHandler.class));
     }
 
     /**
@@ -163,6 +168,35 @@ public class CommandHandler {
      */
     public CommandHandler(IgniteLogger logger) {
         this.logger = logger;
+        Iterable<CommandsProvider> it = U.loadService(CommandsProvider.class);
+
+        Map<String, Command<?>> cmds = new LinkedHashMap<>();
+
+        CommandList.commands().forEach((k, v) -> cmds.put(k.toLowerCase(), v));
+
+        if (!F.isEmpty(it)) {
+            for (CommandsProvider provider : it) {
+                if (logger.isDebugEnabled())
+                    logger.debug("Registering pluggable commands provider: " + 
provider);
+
+                provider.commands().forEach((k, v) -> {
+                    k = k.toLowerCase();
+
+                    if (logger.isDebugEnabled())
+                        logger.debug("Registering command: " + k);
+
+                    if (cmds.containsKey(k)) {
+                        throw new IllegalArgumentException("Found conflict for 
command " + k + ". Provider " +
+                            provider + " tries to register command " + v + ", 
but this command has already been " +
+                            "registered " + cmds.get(k));
+                    }
+                    else
+                        cmds.put(k, v);
+                });
+            }
+        }
+
+        this.cmds = Collections.unmodifiableMap(cmds);
     }
 
     /**
@@ -195,7 +229,7 @@ public class CommandHandler {
 
             verbose = F.exist(rawArgs, CMD_VERBOSE::equalsIgnoreCase);
 
-            ConnectionAndSslParameters args = new 
CommonArgParser(logger).parseAndValidate(rawArgs.iterator());
+            ConnectionAndSslParameters args = new CommonArgParser(logger, 
cmds).parseAndValidate(rawArgs.iterator());
 
             Command command = args.command();
             commandName = command.name();
@@ -705,9 +739,10 @@ public class CommandHandler {
 
         logger.info("This utility can do the following commands:");
 
-        Arrays.stream(CommandList.values())
-            .filter(c -> experimentalEnabled || !c.command().experimental())
-            .forEach(c -> c.command().printUsage(logger));
+        cmds.values().forEach(c -> {
+            if (experimentalEnabled || !c.experimental())
+                c.printUsage(logger);
+        });
 
         logger.info("");
         logger.info("By default commands affecting the cluster require 
interactive confirmation.");
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java
index 2c7b78ce9bd..feabcdf3ac1 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java
@@ -17,6 +17,10 @@
 
 package org.apache.ignite.internal.commandline;
 
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
 import org.apache.ignite.internal.commandline.cache.CacheCommands;
 import org.apache.ignite.internal.commandline.cdc.CdcCommand;
 import org.apache.ignite.internal.commandline.consistency.ConsistencyCommand;
@@ -116,28 +120,23 @@ public enum CommandList {
     private final String text;
 
     /** Command implementation. */
-    private final Command command;
+    private final Command<?> command;
 
     /**
      * @param text Text.
      * @param command Command implementation.
      */
-    CommandList(String text, Command command) {
+    CommandList(String text, Command<?> command) {
         this.text = text;
         this.command = command;
     }
 
     /**
-     * @param text Command text.
-     * @return Command for the text.
+     * @return Map with commands.
      */
-    public static CommandList of(String text) {
-        for (CommandList cmd : VALUES) {
-            if (cmd.text().equalsIgnoreCase(text))
-                return cmd;
-        }
-
-        return null;
+    public static Map<String, Command<?>> commands() {
+        return Arrays.stream(VALUES).collect(
+            Collectors.toMap(CommandList::text, CommandList::command, (a, b) 
-> a, LinkedHashMap::new));
     }
 
     /**
@@ -150,7 +149,7 @@ public enum CommandList {
     /**
      * @return Command implementation.
      */
-    public Command command() {
+    public Command<?> command() {
         return command;
     }
 
diff --git 
a/modules/extdata/logo/src/test/java/org/apache/ignite/internal/testsuites/IgniteLogoExtensionTestSuite.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandsProvider.java
similarity index 55%
copy from 
modules/extdata/logo/src/test/java/org/apache/ignite/internal/testsuites/IgniteLogoExtensionTestSuite.java
copy to 
modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandsProvider.java
index 36fea9f9d2f..f9ee95175c1 100644
--- 
a/modules/extdata/logo/src/test/java/org/apache/ignite/internal/testsuites/IgniteLogoExtensionTestSuite.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandsProvider.java
@@ -1,12 +1,12 @@
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
+ * 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
+ * the License. You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -15,18 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.testsuites;
+package org.apache.ignite.internal.commandline;
 
-import org.apache.ignite.internal.IgniteExtendedLogoTest;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import java.util.Map;
 
 /**
- * Info provider test suite.
+ * Pluggable Ignite component that is responsible for providing list of 
commands for control utility.
  */
-@RunWith(Suite.class)
[email protected]({
-    IgniteExtendedLogoTest.class
-})
-public class IgniteLogoExtensionTestSuite {
+public interface CommandsProvider {
+    /** Gets all supported by this provider commands. */
+    public Map<String, Command<?>> commands();
 }
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommonArgParser.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommonArgParser.java
index 238a3a78046..91257db88ee 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommonArgParser.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommonArgParser.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
@@ -44,6 +45,9 @@ public class CommonArgParser {
     /** */
     private final IgniteLogger logger;
 
+    /** */
+    private final Map<String, Command<?>> cmds;
+
     /** */
     static final String CMD_HOST = "--host";
 
@@ -146,9 +150,11 @@ public class CommonArgParser {
 
     /**
      * @param logger Logger.
+     * @param cmds Supported commands.
      */
-    public CommonArgParser(IgniteLogger logger) {
+    public CommonArgParser(IgniteLogger logger, Map<String, Command<?>> cmds) {
         this.logger = logger;
+        this.cmds = cmds;
     }
 
     /**
@@ -224,26 +230,25 @@ public class CommonArgParser {
 
         boolean experimentalEnabled = 
IgniteSystemProperties.getBoolean(IGNITE_ENABLE_EXPERIMENTAL_COMMAND);
 
-        CommandArgIterator argIter = new CommandArgIterator(rawArgIter, 
AUX_COMMANDS);
+        CommandArgIterator argIter = new CommandArgIterator(rawArgIter, 
AUX_COMMANDS, cmds);
 
-        CommandList command = null;
+        Command<?> command = null;
 
         while (argIter.hasNextArg()) {
             String str = argIter.nextArg("").toLowerCase();
 
-            CommandList cmd = CommandList.of(str);
+            Command<?> cmd = cmds.get(str);
 
             if (cmd != null) {
                 if (command != null)
                     throw new IllegalArgumentException("Only one action can be 
specified, but found at least two:" +
                         cmd.toString() + ", " + command.toString());
 
-                cmd.command().parseArguments(argIter);
+                cmd.parseArguments(argIter);
 
                 command = cmd;
             }
             else {
-
                 switch (str) {
                     case CMD_HOST:
                         host = argIter.nextArg("Expected host name");
@@ -358,14 +363,14 @@ public class CommonArgParser {
         if (command == null)
             throw new IllegalArgumentException("No action was specified");
 
-        if (!experimentalEnabled && command.command().experimental()) {
+        if (!experimentalEnabled && command.experimental()) {
             logger.warning(String.format("To use experimental command add 
--enable-experimental parameter for %s",
                 UTILITY_NAME));
 
             throw new IllegalArgumentException("Experimental commands 
disabled");
         }
 
-        return new ConnectionAndSslParameters(command.command(), host, port, 
user, pwd,
+        return new ConnectionAndSslParameters(command, host, port, user, pwd,
                 pingTimeout, pingInterval, autoConfirmation, verbose,
                 sslProtocol, sslCipherSuites,
                 sslKeyAlgorithm, sslKeyStorePath, sslKeyStorePassword, 
sslKeyStoreType,
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommandList.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommandList.java
deleted file mode 100644
index 9e0838627ed..00000000000
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommandList.java
+++ /dev/null
@@ -1,166 +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.internal.commandline.cache;
-
-import org.apache.ignite.internal.commandline.Command;
-import org.jetbrains.annotations.Nullable;
-
-/**
- *
- */
-public enum CacheCommandList {
-    /**
-     * Prints out help for the cache command.
-     */
-    HELP("help", null),
-
-    /**
-     * Checks consistency of primary and backup partitions assuming no 
concurrent updates are happening in the cluster.
-     */
-    IDLE_VERIFY("idle_verify", new IdleVerify()),
-
-    /**
-     * Prints info regarding caches, groups or sequences.
-     */
-    LIST("list", new CacheViewer()),
-
-    /**
-     * Destroy caches.
-     */
-    DESTROY("destroy", new CacheDestroy()),
-
-    /**
-     * Clear caches.
-     */
-    CLEAR("clear", new CacheClear()),
-
-    /**
-     * Validates indexes attempting to read each indexed entry.
-     */
-    VALIDATE_INDEXES("validate_indexes", new CacheValidateIndexes()),
-
-    /**
-     * Check secondary indexes inline size.
-     */
-    CHECK_INDEX_INLINE_SIZES("check_index_inline_sizes", new 
CheckIndexInlineSizes()),
-
-    /**
-     * Prints info about contended keys (the keys concurrently locked from 
multiple transactions).
-     */
-    CONTENTION("contention", new CacheContention()),
-
-    /**
-     * Collect information on the distribution of partitions.
-     */
-    DISTRIBUTION("distribution", new CacheDistribution()),
-
-    /**
-     * Reset lost partitions
-     */
-    RESET_LOST_PARTITIONS("reset_lost_partitions", new ResetLostPartitions()),
-
-    /**
-     * Find and remove garbage.
-     */
-    FIND_AND_DELETE_GARBAGE("find_garbage", new FindAndDeleteGarbage()),
-
-    /**
-     * Index list.
-     */
-    INDEX_LIST("indexes_list", new CacheIndexesList()),
-
-    /**
-     * Index rebuild status.
-     */
-    INDEX_REBUILD_STATUS("indexes_rebuild_status", new 
CacheIndexesRebuildStatus()),
-
-    /**
-     * Index force rebuild.
-     */
-    INDEX_FORCE_REBUILD("indexes_force_rebuild", new 
CacheIndexesForceRebuild()),
-
-    /**
-     * Enable, disable or show status for cache metrics.
-     */
-    METRICS("metrics", new CacheMetrics()),
-
-    /**
-     * Schedule index rebuild via the maintenance mode.
-     */
-    INDEX_REBUILD("schedule_indexes_rebuild", new 
CacheScheduleIndexesRebuild());
-
-    /** Enumerated values. */
-    private static final CacheCommandList[] VALS = values();
-
-    /** Name. */
-    private final String name;
-
-    /** */
-    private final Command command;
-
-    /**
-     * @param name Name.
-     * @param command Command implementation.
-     */
-    CacheCommandList(String name, Command command) {
-        this.name = name;
-        this.command = command;
-    }
-
-    /**
-     * @param text Command text.
-     * @return Command for the text.
-     */
-    public static CacheCommandList of(String text) {
-        for (CacheCommandList cmd : CacheCommandList.values()) {
-            if (cmd.text().equalsIgnoreCase(text))
-                return cmd;
-        }
-
-        return null;
-    }
-
-    /**
-     * @return Name.
-     */
-    public String text() {
-        return name;
-    }
-
-    /**
-     * @return Cache subcommand implementation.
-     */
-    public Command subcommand() {
-        return command;
-    }
-
-    /**
-     * Efficiently gets enumerated value from its ordinal.
-     *
-     * @param ord Ordinal value.
-     * @return Enumerated value or {@code null} if ordinal out of range.
-     */
-    @Nullable public static CacheCommandList fromOrdinal(int ord) {
-        return ord >= 0 && ord < VALS.length ? VALS[ord] : null;
-    }
-
-    /** {@inheritDoc} */
-    @Override public String toString() {
-        return name;
-    }
-}
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommands.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommands.java
index b4ae21be3a1..f94b9fc2cdd 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommands.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommands.java
@@ -115,7 +115,7 @@ public class CacheCommands extends 
AbstractCommand<CacheSubcommands> {
         logger.info("");
         logger.info(INDENT + "Subcommands:");
 
-        Arrays.stream(CacheCommandList.values()).forEach(c -> {
+        Arrays.stream(CacheSubcommands.values()).forEach(c -> {
             if (c.subcommand() != null) c.subcommand().printUsage(logger);
         });
 
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java
index 8b64f8f4727..571b8129229 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java
@@ -49,11 +49,26 @@ public enum CacheSubcommands {
      */
     LIST("list", ListCommandArg.class, new CacheViewer()),
 
+    /**
+     * Destroy caches.
+     */
+    DESTROY("destroy", null, new CacheDestroy()),
+
+    /**
+     * Clear caches.
+     */
+    CLEAR("clear", null, new CacheClear()),
+
     /**
      * Validates indexes attempting to read each indexed entry.
      */
     VALIDATE_INDEXES("validate_indexes", ValidateIndexesCommandArg.class, new 
CacheValidateIndexes()),
 
+    /**
+     * Check secondary indexes inline size.
+     */
+    CHECK_INDEX_INLINE_SIZES("check_index_inline_sizes", null, new 
CheckIndexInlineSizes()),
+
     /**
      * Prints info about contended keys (the keys concurrently locked from 
multiple transactions).
      */
@@ -89,30 +104,15 @@ public enum CacheSubcommands {
      */
     INDEX_FORCE_REBUILD("indexes_force_rebuild", 
IndexForceRebuildCommandArg.class, new CacheIndexesForceRebuild()),
 
-    /**
-     * Index rebuild via the maintenance mode.
-     */
-    INDEX_REBUILD("schedule_indexes_rebuild", IndexRebuildCommandArg.class, 
new CacheScheduleIndexesRebuild()),
-
-    /**
-     * Check secondary indexes inline size.
-     */
-    CHECK_INDEX_INLINE_SIZES("check_index_inline_sizes", null, new 
CheckIndexInlineSizes()),
-
-    /**
-     * Destroy caches.
-     */
-    DESTROY("destroy", null, new CacheDestroy()),
-
     /**
      * Enable / disable cache metrics collection or show metrics collection 
status.
      */
     METRICS("metrics", null, new CacheMetrics()),
 
     /**
-     * Clear caches.
+     * Index rebuild via the maintenance mode.
      */
-    CLEAR("clear", null, new CacheClear());
+    INDEX_REBUILD("schedule_indexes_rebuild", IndexRebuildCommandArg.class, 
new CacheScheduleIndexesRebuild());
 
     /** Enumerated values. */
     private static final CacheSubcommands[] VALS = values();
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
index c2ee56ce044..b06ac82fb4b 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
@@ -1189,7 +1189,7 @@ public class CommandHandlerParsingTest {
      * @return Common parameters container object.
      */
     private ConnectionAndSslParameters parseArgs(List<String> args) {
-        return new CommonArgParser(setupTestLogger()).
+        return new CommonArgParser(setupTestLogger(), CommandList.commands()).
             parseAndValidate(args.iterator());
     }
 
diff --git a/modules/extdata/logo/licenses/apache-2.0.txt 
b/modules/extdata/pluggable/licenses/apache-2.0.txt
similarity index 100%
rename from modules/extdata/logo/licenses/apache-2.0.txt
rename to modules/extdata/pluggable/licenses/apache-2.0.txt
diff --git a/modules/extdata/logo/pom.xml b/modules/extdata/pluggable/pom.xml
similarity index 89%
rename from modules/extdata/logo/pom.xml
rename to modules/extdata/pluggable/pom.xml
index 3cddb24d8cc..fa93410f4bc 100644
--- a/modules/extdata/logo/pom.xml
+++ b/modules/extdata/pluggable/pom.xml
@@ -29,7 +29,7 @@
         <relativePath>../../../parent-internal/pom.xml</relativePath>
     </parent>
 
-    <artifactId>ignite-extdata-logo</artifactId>
+    <artifactId>ignite-extdata-pluggable</artifactId>
     <url>https://ignite.apache.org</url>
 
     <dependencies>
@@ -78,6 +78,19 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-control-utility</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ignite-control-utility</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
             <artifactId>log4j-core</artifactId>
diff --git 
a/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/CommandsProviderExtImpl.java
 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/CommandsProviderExtImpl.java
new file mode 100644
index 00000000000..06282f7372a
--- /dev/null
+++ 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/CommandsProviderExtImpl.java
@@ -0,0 +1,90 @@
+/*
+ * 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.internal.commandline;
+
+import java.util.Collections;
+import java.util.Map;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+
+/**
+ * Additional commands provider for control utility.
+ */
+public class CommandsProviderExtImpl implements CommandsProvider {
+    /** */
+    public static final Command<?> TEST_COMMAND = new TestCommand();
+
+    /** */
+    public static final String TEST_COMMAND_OUTPUT = "Test command executed";
+
+    /** */
+    public static final String TEST_COMMAND_USAGE = "Test command usage.";
+
+    /** */
+    public static final String TEST_COMMAND_ARG = "test-print";
+
+    /** {@inheritDoc} */
+    @Override public Map<String, Command<?>> commands() {
+        return Collections.singletonMap(TEST_COMMAND.name(), TEST_COMMAND);
+    }
+
+    /** */
+    public static class TestCommand extends AbstractCommand<Object> {
+        /** */
+        private String arg;
+
+        /** {@inheritDoc} */
+        @Override public Object execute(GridClientConfiguration clientCfg, 
IgniteLogger log) {
+            log.info(TEST_COMMAND_OUTPUT + ": " + arg);
+
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void parseArguments(CommandArgIterator argIter) {
+            String cmdArg = argIter.nextArg("Required 1-st argument");
+
+            if (TEST_COMMAND_ARG.equals(cmdArg))
+                arg = argIter.nextArg("Required 2-nd argument");
+            else
+                throw new IllegalArgumentException("Invalid argument \"" + 
cmdArg + "\".");
+
+            if (argIter.hasNextSubArg()) {
+                throw new IllegalArgumentException(
+                    "Invalid argument \"" + argIter.peekNextArg() + "\", no 
more arguments is expected.");
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object arg() {
+            return arg;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void printUsage(IgniteLogger log) {
+            usage(log, TEST_COMMAND_USAGE, TEST_COMMAND.name(), 
F.asMap("value", "Value to print"),
+                TEST_COMMAND_ARG, "value");
+        }
+
+        /** {@inheritDoc} */
+        @Override public String name() {
+            return "--test-command";
+        }
+    }
+}
diff --git 
a/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/ExtendedControlUtilityTest.java
 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/ExtendedControlUtilityTest.java
new file mode 100644
index 00000000000..8631898b274
--- /dev/null
+++ 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/ExtendedControlUtilityTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.internal.commandline;
+
+import org.apache.ignite.util.GridCommandHandlerAbstractTest;
+import org.junit.Test;
+
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_INVALID_ARGUMENTS;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static org.apache.ignite.testframework.GridTestUtils.assertContains;
+
+/**
+ * Tests control-utility extension.
+ */
+public class ExtendedControlUtilityTest extends GridCommandHandlerAbstractTest 
{
+    /**
+     * Tests additional command for control-utility works.
+     */
+    @Test
+    public void testAdditionalCommand() {
+        autoConfirmation = false;
+
+        injectTestSystemOut();
+
+        String testVal = "test value";
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, 
execute(CommandsProviderExtImpl.TEST_COMMAND.name()));
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, 
execute(CommandsProviderExtImpl.TEST_COMMAND.name(),
+            CommandsProviderExtImpl.TEST_COMMAND_ARG));
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, 
execute(CommandsProviderExtImpl.TEST_COMMAND.name(),
+            "unknownSubcommand", testVal));
+
+        assertEquals(EXIT_CODE_OK, 
execute(CommandsProviderExtImpl.TEST_COMMAND.name(),
+            CommandsProviderExtImpl.TEST_COMMAND_ARG, testVal));
+
+        assertContains(log, testOut.toString(), 
CommandsProviderExtImpl.TEST_COMMAND_OUTPUT);
+        assertContains(log, testOut.toString(), testVal);
+    }
+
+    /**
+     * Tests usage help for additional commands.
+     */
+    @Test
+    public void testAdditionalCommandHelp() {
+        injectTestSystemOut();
+
+        assertEquals(EXIT_CODE_OK, execute("--help"));
+
+        String testOutStr = testOut.toString();
+
+        assertContains(log, testOutStr, 
CommandsProviderExtImpl.TEST_COMMAND_USAGE);
+        assertContains(log, testOutStr, 
CommandsProviderExtImpl.TEST_COMMAND_ARG);
+    }
+}
diff --git 
a/modules/extdata/logo/src/test/java/org/apache/ignite/internal/IgniteExtendedLogoTest.java
 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/plugin/ExtendedLogoTest.java
similarity index 93%
rename from 
modules/extdata/logo/src/test/java/org/apache/ignite/internal/IgniteExtendedLogoTest.java
rename to 
modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/plugin/ExtendedLogoTest.java
index 497d6202c3f..7bc7c7553d2 100644
--- 
a/modules/extdata/logo/src/test/java/org/apache/ignite/internal/IgniteExtendedLogoTest.java
+++ 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/plugin/ExtendedLogoTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal;
+package org.apache.ignite.internal.plugin;
 
 import org.apache.ignite.testframework.ListeningTestLogger;
 import org.apache.ignite.testframework.LogListener;
@@ -25,7 +25,7 @@ import org.junit.Test;
 import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
 
 /** */
-public class IgniteExtendedLogoTest extends GridCommonAbstractTest {
+public class ExtendedLogoTest extends GridCommonAbstractTest {
     /** @throws Exception If fails. */
     @Test
     public void testExtendedLogo() throws Exception {
diff --git 
a/modules/extdata/logo/src/test/java/org/apache/ignite/internal/plugin/IgniteExtLogInfoProviderImpl.java
 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/plugin/IgniteExtLogInfoProviderImpl.java
similarity index 100%
rename from 
modules/extdata/logo/src/test/java/org/apache/ignite/internal/plugin/IgniteExtLogInfoProviderImpl.java
rename to 
modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/plugin/IgniteExtLogInfoProviderImpl.java
diff --git 
a/modules/extdata/logo/src/test/java/org/apache/ignite/internal/testsuites/IgniteLogoExtensionTestSuite.java
 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/testsuites/IgnitePluggableExtensionsTestSuite.java
similarity index 77%
rename from 
modules/extdata/logo/src/test/java/org/apache/ignite/internal/testsuites/IgniteLogoExtensionTestSuite.java
rename to 
modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/testsuites/IgnitePluggableExtensionsTestSuite.java
index 36fea9f9d2f..a22175e865c 100644
--- 
a/modules/extdata/logo/src/test/java/org/apache/ignite/internal/testsuites/IgniteLogoExtensionTestSuite.java
+++ 
b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/testsuites/IgnitePluggableExtensionsTestSuite.java
@@ -17,16 +17,18 @@
 
 package org.apache.ignite.internal.testsuites;
 
-import org.apache.ignite.internal.IgniteExtendedLogoTest;
+import org.apache.ignite.internal.commandline.ExtendedControlUtilityTest;
+import org.apache.ignite.internal.plugin.ExtendedLogoTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
 /**
- * Info provider test suite.
+ * Pluggable extensions test suite.
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-    IgniteExtendedLogoTest.class
+    ExtendedLogoTest.class,
+    ExtendedControlUtilityTest.class,
 })
-public class IgniteLogoExtensionTestSuite {
+public class IgnitePluggableExtensionsTestSuite {
 }
diff --git 
a/modules/extdata/pluggable/src/test/resources/META-INF/services/org.apache.ignite.internal.commandline.CommandsProvider
 
b/modules/extdata/pluggable/src/test/resources/META-INF/services/org.apache.ignite.internal.commandline.CommandsProvider
new file mode 100644
index 00000000000..a73af84557e
--- /dev/null
+++ 
b/modules/extdata/pluggable/src/test/resources/META-INF/services/org.apache.ignite.internal.commandline.CommandsProvider
@@ -0,0 +1 @@
+org.apache.ignite.internal.commandline.CommandsProviderExtImpl
diff --git 
a/modules/extdata/logo/src/test/resources/META-INF/services/org.apache.ignite.internal.plugin.IgniteLogInfoProvider
 
b/modules/extdata/pluggable/src/test/resources/META-INF/services/org.apache.ignite.internal.plugin.IgniteLogInfoProvider
similarity index 100%
rename from 
modules/extdata/logo/src/test/resources/META-INF/services/org.apache.ignite.internal.plugin.IgniteLogInfoProvider
rename to 
modules/extdata/pluggable/src/test/resources/META-INF/services/org.apache.ignite.internal.plugin.IgniteLogInfoProvider
diff --git a/pom.xml b/pom.xml
index 7b1c60baec6..09611276dad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
         <module>modules/extdata/p2p</module>
         <module>modules/extdata/uri</module>
         <module>modules/extdata/platform</module>
-        <module>modules/extdata/logo</module>
+        <module>modules/extdata/pluggable</module>
         <module>modules/clients</module>
         <module>modules/spring</module>
         <module>modules/web</module>


Reply via email to