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 7b7a0ae02a9 IGNITE-18320 Add cache scan command to control-utility - 
Fixes #10628.
7b7a0ae02a9 is described below

commit 7b7a0ae02a981d317035e4ba6cb8dd1c0b1ae56a
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Fri Apr 7 14:47:31 2023 +0300

    IGNITE-18320 Add cache scan command to control-utility - Fixes #10628.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 docs/_docs/tools/control-script.adoc               |  26 +++
 .../internal/commandline/cache/CacheScan.java      | 128 ++++++++++++++
 .../commandline/cache/CacheSubcommands.java        |   7 +-
 .../util/GridCommandHandlerClusterByClassTest.java |  73 ++++++++
 .../internal/visor/cache/VisorCacheScanTask.java   | 192 +++++++++++++++++++++
 .../visor/cache/VisorCacheScanTaskArg.java         |  86 +++++++++
 .../visor/cache/VisorCacheScanTaskResult.java      |  86 +++++++++
 .../main/resources/META-INF/classnames.properties  |   4 +
 ...mandHandlerClusterByClassTest_cache_help.output |   6 +
 ...dlerClusterByClassWithSSLTest_cache_help.output |   6 +
 10 files changed, 613 insertions(+), 1 deletion(-)

diff --git a/docs/_docs/tools/control-script.adoc 
b/docs/_docs/tools/control-script.adoc
index 5eed17adb0a..cc90f81ec05 100644
--- a/docs/_docs/tools/control-script.adoc
+++ b/docs/_docs/tools/control-script.adoc
@@ -473,6 +473,32 @@ Examples:
 control.sh|bat --cache clear --caches cache1,cache2
 ----
 
+== Scanning Caches
+
+You can use the control script to scan cache entries.
+
+[source, shell]
+----
+control.sh|bat --cache scan cacheName [--limit N]
+----
+
+For each entry four columns will be displayed: key class, string 
representation of key, value class, and string representation of value.
+
+Parameters:
+
+[cols="1,3",opts="header"]
+|===
+| Parameter | Description
+| `--limit N`| Limit amount of entries to scan (default 1000).
+|===
+
+Examples:
+[source, shell]
+----
+# Query no more than 10 entries from cache "cache1"
+control.sh|bat --cache scan cache1 --limit 10
+----
+
 == Resetting Lost Partitions
 
 You can use the control script to reset lost partitions for specific caches.
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheScan.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheScan.java
new file mode 100644
index 00000000000..76f5195a7a1
--- /dev/null
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheScan.java
@@ -0,0 +1,128 @@
+/*
+ * 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 java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.Command;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.TaskExecutor;
+import org.apache.ignite.internal.commandline.systemview.SystemViewCommand;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.visor.cache.VisorCacheScanTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheScanTaskArg;
+import org.apache.ignite.internal.visor.cache.VisorCacheScanTaskResult;
+import org.apache.ignite.internal.visor.systemview.VisorSystemViewTask;
+
+import static org.apache.ignite.internal.commandline.CommandLogger.optional;
+import static 
org.apache.ignite.internal.commandline.cache.CacheSubcommands.SCAN;
+
+/**
+ * Command to show caches on cluster.
+ */
+public class CacheScan extends AbstractCommand<CacheScan.Arguments> {
+    /** Default entries limit. */
+    private static final int DFLT_LIMIT = 1_000;
+
+    /** {@inheritDoc} */
+    @Override public void printUsage(IgniteLogger log) {
+        String description = "Show cache content.";
+
+        Map<String, String> map = F.asMap("--limit", "limit count of entries 
to scan (" + DFLT_LIMIT + " by default)");
+
+        usageCache(log, SCAN, description, map, "cacheName", 
optional("--limit", "N"));
+    }
+
+    /** Command parsed arguments */
+    private Arguments args;
+
+    /** {@inheritDoc} */
+    @Override public Arguments arg() {
+        return args;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object execute(GridClientConfiguration clientCfg, 
IgniteLogger log) throws Exception {
+        VisorCacheScanTaskArg taskArg = new 
VisorCacheScanTaskArg(args.cacheName, args.limit);
+
+        VisorCacheScanTaskResult res;
+
+        try (GridClient client = Command.startClient(clientCfg)) {
+            res = TaskExecutor.executeTask(
+                client,
+                VisorCacheScanTask.class,
+                taskArg,
+                clientCfg
+            );
+
+            List<VisorSystemViewTask.SimpleType> types = res.titles().stream()
+                .map(x -> 
VisorSystemViewTask.SimpleType.STRING).collect(Collectors.toList());
+
+            SystemViewCommand.printTable(res.titles(), types, res.entries(), 
log);
+
+            if (res.entries().size() == args.limit)
+                log.info("Result limited to " + args.limit + " rows. Limit can 
be changed with '--limit' argument.");
+        }
+
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void parseArguments(CommandArgIterator argIter) {
+        String cacheName = argIter.nextArg("Cache name is expected");
+        int limit = DFLT_LIMIT;
+
+        while (argIter.hasNextSubArg()) {
+            String nextArg = argIter.nextArg("").toLowerCase();
+
+            if ("--limit".equals(nextArg))
+                limit = argIter.nextIntArg("limit");
+            else
+                throw new IllegalArgumentException("Unknown argument: " + 
nextArg);
+        }
+
+        args = new Arguments(cacheName, limit);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String name() {
+        return SCAN.text().toUpperCase();
+    }
+
+    /**
+     * Container for command arguments.
+     */
+    public static class Arguments {
+        /** Cache name. */
+        private final String cacheName;
+
+        /** Rows limit. */
+        private final int limit;
+
+        /** */
+        public Arguments(String cacheName, int limit) {
+            this.cacheName = cacheName;
+            this.limit = limit;
+        }
+    }
+}
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 571b8129229..33cb67613fc 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
@@ -112,7 +112,12 @@ public enum CacheSubcommands {
     /**
      * Index rebuild via the maintenance mode.
      */
-    INDEX_REBUILD("schedule_indexes_rebuild", IndexRebuildCommandArg.class, 
new CacheScheduleIndexesRebuild());
+    INDEX_REBUILD("schedule_indexes_rebuild", IndexRebuildCommandArg.class, 
new CacheScheduleIndexesRebuild()),
+
+    /**
+     * Scan cache entries.
+     */
+    SCAN("scan", IndexRebuildCommandArg.class, new CacheScan());
 
     /** Enumerated values. */
     private static final CacheSubcommands[] VALS = values();
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
index a3cf746a826..769cd10944c 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
@@ -24,6 +24,7 @@ import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
 import java.util.EnumMap;
 import java.util.HashSet;
 import java.util.List;
@@ -119,6 +120,7 @@ import static 
org.apache.ignite.internal.commandline.cache.CacheDestroy.DESTROY_
 import static 
org.apache.ignite.internal.commandline.cache.CacheSubcommands.CLEAR;
 import static 
org.apache.ignite.internal.commandline.cache.CacheSubcommands.DESTROY;
 import static 
org.apache.ignite.internal.commandline.cache.CacheSubcommands.HELP;
+import static 
org.apache.ignite.internal.commandline.cache.CacheSubcommands.SCAN;
 import static 
org.apache.ignite.internal.commandline.cdc.DeleteLostSegmentLinksCommand.DELETE_LOST_SEGMENT_LINKS;
 import static 
org.apache.ignite.internal.commandline.cdc.DeleteLostSegmentLinksCommand.NODE_ID;
 import static 
org.apache.ignite.internal.commandline.consistency.ConsistencyCommand.CACHE;
@@ -1437,6 +1439,62 @@ public class GridCommandHandlerClusterByClassTest 
extends GridCommandHandlerClus
         assertContains(log, out, "affCls=RendezvousAffinityFunction");
     }
 
+    /** */
+    @Test
+    public void testCacheScan() {
+        injectTestSystemOut();
+
+        autoConfirmation = false;
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", 
SCAN.text()));
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", 
SCAN.text(), "cacheX", "test"));
+
+        assertContains(log, testOut.toString(), "Unknown argument: test");
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", 
SCAN.text(), "cache", "--limit"));
+
+        if (sslEnabled()) // Extra arguments at the end added.
+            assertContains(log, testOut.toString(), "Invalid value for limit");
+        else
+            assertContains(log, testOut.toString(), "Expecting limit");
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", 
SCAN.text(), "cache", "--limit", "test"));
+
+        assertContains(log, testOut.toString(), "Invalid value for limit: 
test");
+
+        assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", 
SCAN.text(), "cache", "--limit", "123", "test"));
+
+        assertContains(log, testOut.toString(), "Unknown argument: test");
+
+        IgniteCache<Integer, Object> c = crd.createCache(new 
CacheConfiguration<>("testCache"));
+
+        c.put(0, 0);
+        c.put(1, "test string");
+        c.put(2, new Date());
+        c.put(3, new byte[] {1, 2, 3});
+        c.put(4, asList("test list 1", "test list 2"));
+        c.put(5, new String[] {"test array 1", "test array 2"});
+        c.put(6, UUID.randomUUID());
+        c.put(7, new TestClass(0, "test class"));
+
+        assertEquals(EXIT_CODE_OK, execute("--cache", SCAN.text(), 
"testCache"));
+
+        assertContains(log, testOut.toString(), "test string");
+        assertContains(log, testOut.toString(), "test list 1");
+        assertContains(log, testOut.toString(), "test array 1");
+        assertContains(log, testOut.toString(), "test class");
+        assertNotContains(log, testOut.toString(), "Result limited");
+
+        assertEquals(EXIT_CODE_OK, execute("--cache", SCAN.text(), 
"testCache", "--limit", "5"));
+
+        assertContains(log, testOut.toString(), "Result limited");
+
+        assertEquals(EXIT_CODE_OK, execute("--cache", SCAN.text(), 
"testCache", "--limit", "10"));
+
+        assertNotContains(log, testOut.toString(), "Result limited");
+    }
+
     /** */
     @Test
     public void testCacheConfigNoOutputFormat() {
@@ -1990,4 +2048,19 @@ public class GridCommandHandlerClusterByClassTest 
extends GridCommandHandlerClus
 
         consumer.accept(testOut.toString());
     }
+
+    /** */
+    private static class TestClass {
+        /** */
+        private final int i;
+
+        /** */
+        private final String s;
+
+        /** */
+        public TestClass(int i, String s) {
+            this.i = i;
+            this.s = s;
+        }
+    }
 }
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTask.java
new file mode 100644
index 00000000000..c5b82b4e674
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTask.java
@@ -0,0 +1,192 @@
+/*
+ * 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.visor.cache;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.cache.Cache;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryType;
+import org.apache.ignite.internal.binary.BinaryObjectEx;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.processors.task.GridVisorManagementTask;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorOneNodeTask;
+
+/**
+ * Task that scan cache entries.
+ */
+@GridInternal
+@GridVisorManagementTask
+public class VisorCacheScanTask extends 
VisorOneNodeTask<VisorCacheScanTaskArg, VisorCacheScanTaskResult> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** {@inheritDoc} */
+    @Override protected VisorCacheScanJob job(VisorCacheScanTaskArg arg) {
+        return new VisorCacheScanJob(arg, debug);
+    }
+
+    /**
+     * Job that stop specified caches.
+     */
+    private static class VisorCacheScanJob extends 
VisorJob<VisorCacheScanTaskArg, VisorCacheScanTaskResult> {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /**
+         * Create job.
+         *
+         * @param arg Task argument.
+         * @param debug Debug flag.
+         */
+        private VisorCacheScanJob(VisorCacheScanTaskArg arg, boolean debug) {
+            super(arg, debug);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected VisorCacheScanTaskResult run(VisorCacheScanTaskArg 
arg) {
+            if (F.isEmpty(arg.getCacheName()))
+                throw new IllegalStateException("Cache name was not 
specified.");
+
+            if (arg.getLimit() <= 0)
+                throw new IllegalStateException("Invalid limit value.");
+
+            List<String> titles = Arrays.asList("Key Class", "Key", "Value 
Class", "Value");
+
+            int cnt = 0;
+            List<List<?>> entries = new ArrayList<>();
+
+            for (Cache.Entry<?, ?> entry : 
ignite.cache(arg.getCacheName()).withKeepBinary()) {
+                Object k = entry.getKey();
+                Object v = entry.getValue();
+                entries.add(Arrays.asList(typeOf(k), valueOf(k), typeOf(v), 
valueOf(v)));
+
+                if (++cnt >= arg.getLimit())
+                    break;
+            }
+
+            return new VisorCacheScanTaskResult(titles, entries);
+        }
+
+        /**
+         * @param o Source object.
+         * @return String representation of object class.
+         */
+        private static String typeOf(Object o) {
+            if (o != null) {
+                Class<?> clazz = o.getClass();
+
+                return clazz.isArray() ? 
IgniteUtils.compact(clazz.getComponentType().getName()) + "[]"
+                    : IgniteUtils.compact(o.getClass().getName());
+            }
+            else
+                return "n/a";
+        }
+
+        /**
+         * @param o Object.
+         * @return String representation of value.
+         */
+        private static String valueOf(Object o) {
+            if (o == null)
+                return "null";
+
+            if (o instanceof byte[])
+                return "size=" + ((byte[])o).length;
+
+            if (o instanceof Byte[])
+                return "size=" + ((Byte[])o).length;
+
+            if (o instanceof Object[]) {
+                return "size=" + ((Object[])o).length +
+                    ", values=[" + S.joinToString(Arrays.asList((Object[])o), 
", ", "...", 120, 0) + "]";
+            }
+
+            if (o instanceof BinaryObject)
+                return binaryToString((BinaryObject)o);
+
+            return o.toString();
+        }
+
+        /**
+         * Convert Binary object to string.
+         *
+         * @param obj Binary object.
+         * @return String representation of Binary object.
+         */
+        public static String binaryToString(BinaryObject obj) {
+            int hash = obj.hashCode();
+
+            if (obj instanceof BinaryObjectEx) {
+                BinaryObjectEx objEx = (BinaryObjectEx)obj;
+
+                BinaryType meta;
+
+                try {
+                    meta = ((BinaryObjectEx)obj).rawType();
+                }
+                catch (BinaryObjectException ignore) {
+                    meta = null;
+                }
+
+                if (meta != null) {
+                    if (meta.isEnum()) {
+                        try {
+                            return obj.deserialize().toString();
+                        }
+                        catch (BinaryObjectException ignore) {
+                            // NO-op.
+                        }
+                    }
+
+                    SB buf = new SB(meta.typeName());
+
+                    if (meta.fieldNames() != null) {
+                        buf.a(" [hash=").a(hash);
+
+                        for (String name : meta.fieldNames()) {
+                            Object val = objEx.field(name);
+
+                            buf.a(", ").a(name).a('=').a(val);
+                        }
+
+                        buf.a(']');
+
+                        return buf.toString();
+                    }
+                }
+            }
+
+            return S.toString(obj.getClass().getSimpleName(),
+                "hash", hash, false,
+                "typeId", obj.type().typeId(), true);
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(VisorCacheScanJob.class, this);
+        }
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTaskArg.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTaskArg.java
new file mode 100644
index 00000000000..01f79b8c9d1
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTaskArg.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.internal.visor.cache;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Argument for {@link VisorCacheStopTask}.
+ */
+public class VisorCacheScanTaskArg extends IgniteDataTransferObject {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Cache name. */
+    private String cacheName;
+
+    /** Entries limit. */
+    private int limit;
+
+    /**
+     * Default constructor.
+     */
+    public VisorCacheScanTaskArg() {
+        // No-op.
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @param limit Entries limit.
+     */
+    public VisorCacheScanTaskArg(String cacheName, int limit) {
+        this.cacheName = cacheName;
+        this.limit = limit;
+    }
+
+    /**
+     * @return Cache name.
+     */
+    public String getCacheName() {
+        return cacheName;
+    }
+
+    /**
+     * @return Entries limit.
+     */
+    public int getLimit() {
+        return limit;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void writeExternalData(ObjectOutput out) throws 
IOException {
+        U.writeString(out, cacheName);
+        out.writeInt(limit);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void readExternalData(byte protoVer, ObjectInput in) 
throws IOException, ClassNotFoundException {
+        cacheName = U.readString(in);
+        limit = in.readInt();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(VisorCacheScanTaskArg.class, this);
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTaskResult.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTaskResult.java
new file mode 100644
index 00000000000..4d93674a76e
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheScanTaskResult.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.internal.visor.cache;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Result of running {@link VisorCacheScanTask}.
+ */
+@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+public class VisorCacheScanTaskResult extends IgniteDataTransferObject {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Column titles. */
+    private List<String> titles;
+
+    /** Cache entries. */
+    private List<List<?>> entries;
+
+    /**
+     *
+     */
+    public VisorCacheScanTaskResult() {
+    }
+
+    /**
+     * @param entries Cache entries.
+     */
+    public VisorCacheScanTaskResult(List<String> titles, List<List<?>> 
entries) {
+        this.titles = titles;
+        this.entries = entries;
+    }
+
+    /**
+     * @return Column titles.
+     */
+    public List<String> titles() {
+        return titles;
+    }
+
+    /**
+     * @return Cache entries.
+     */
+    public List<List<?>> entries() {
+        return entries;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void writeExternalData(ObjectOutput out) throws 
IOException {
+        U.writeCollection(out, titles);
+        U.writeCollection(out, entries);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void readExternalData(byte protoVer, ObjectInput in) 
throws IOException, ClassNotFoundException {
+        titles = U.readList(in);
+        entries = U.readList(in);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(VisorCacheScanTaskResult.class, this);
+    }
+}
diff --git a/modules/core/src/main/resources/META-INF/classnames.properties 
b/modules/core/src/main/resources/META-INF/classnames.properties
index d509b8248b2..5a97fc38575 100644
--- a/modules/core/src/main/resources/META-INF/classnames.properties
+++ b/modules/core/src/main/resources/META-INF/classnames.properties
@@ -2096,6 +2096,10 @@ 
org.apache.ignite.internal.visor.cache.VisorFindAndDeleteGarbageInPersistenceTas
 
org.apache.ignite.internal.visor.cache.VisorFindAndDeleteGarbageInPersistenceTask$FindAndDeleteGarbageInPersistenceJob
 
org.apache.ignite.internal.visor.cache.VisorFindAndDeleteGarbageInPersistenceTaskArg
 
org.apache.ignite.internal.visor.cache.VisorFindAndDeleteGarbageInPersistenceTaskResult
+org.apache.ignite.internal.visor.cache.VisorCacheScanTask
+org.apache.ignite.internal.visor.cache.VisorCacheScanTask$VisorCacheScanJob
+org.apache.ignite.internal.visor.cache.VisorCacheScanTaskArg
+org.apache.ignite.internal.visor.cache.VisorCacheScanTaskResult
 org.apache.ignite.internal.visor.cache.index.IndexForceRebuildTask
 
org.apache.ignite.internal.visor.cache.index.IndexForceRebuildTask$IndexForceRebuildJob
 org.apache.ignite.internal.visor.cache.index.IndexForceRebuildTaskArg
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output
index 1402916d4e7..ffe0491c120 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output
@@ -104,6 +104,12 @@ Arguments: --cache help --yes
       --cache-names  - Comma-separated list of cache names with optionally 
specified indexes. If indexes are not specified then all indexes of the cache 
will be scheduled for the rebuild operation. Can be used simultaneously with 
cache group names.
       --group-names  - Comma-separated list of cache group names for which 
indexes should be scheduled for the rebuild. Can be used simultaneously with 
cache names.
 
+  --cache scan cacheName [--limit N]
+    Show cache content.
+
+    Parameters:
+      --limit  - limit count of entries to scan (1000 by default)
+
 Command [CACHE] finished with code: 0
 Control utility has completed execution at: <!any!>
 Execution time: <!any!>
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output
index 1402916d4e7..ffe0491c120 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output
@@ -104,6 +104,12 @@ Arguments: --cache help --yes
       --cache-names  - Comma-separated list of cache names with optionally 
specified indexes. If indexes are not specified then all indexes of the cache 
will be scheduled for the rebuild operation. Can be used simultaneously with 
cache group names.
       --group-names  - Comma-separated list of cache group names for which 
indexes should be scheduled for the rebuild. Can be used simultaneously with 
cache names.
 
+  --cache scan cacheName [--limit N]
+    Show cache content.
+
+    Parameters:
+      --limit  - limit count of entries to scan (1000 by default)
+
 Command [CACHE] finished with code: 0
 Control utility has completed execution at: <!any!>
 Execution time: <!any!>

Reply via email to