# ignite-gg-9830 WIP

Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/3a2c39f2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/3a2c39f2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/3a2c39f2

Branch: refs/heads/ignite-789
Commit: 3a2c39f21500ee21bbb66ef73c5b62f724126a25
Parents: ce0d9dc
Author: Andrey <anovi...@gridgain.com>
Authored: Thu Apr 23 13:43:08 2015 +0700
Committer: Andrey <anovi...@gridgain.com>
Committed: Thu Apr 23 16:37:43 2015 +0700

----------------------------------------------------------------------
 .../node/VisorNodeEventsCollectorTask.java      |  52 +-
 .../internal/visor/util/VisorEventMapper.java   |  13 +
 .../internal/visor/util/VisorTaskUtils.java     |   2 +-
 .../ignite/visor/commands/VisorConsole.scala    | 308 ++++++-----
 .../visor/commands/VisorConsoleCommand.scala    |  77 ---
 .../ignite/visor/commands/VisorTextTable.scala  | 541 ------------------
 .../visor/commands/ack/VisorAckCommand.scala    |  26 +-
 .../commands/alert/VisorAlertCommand.scala      |  35 +-
 .../commands/cache/VisorCacheClearCommand.scala |  12 +-
 .../commands/cache/VisorCacheCommand.scala      |  13 +-
 .../commands/cache/VisorCacheScanCommand.scala  |   7 +-
 .../commands/cache/VisorCacheSwapCommand.scala  |   8 +-
 .../commands/common/VisorConsoleCommand.scala   |  66 +++
 .../visor/commands/common/VisorTextTable.scala  | 543 +++++++++++++++++++
 .../config/VisorConfigurationCommand.scala      | 132 ++---
 .../commands/deploy/VisorDeployCommand.scala    |  47 +-
 .../commands/disco/VisorDiscoveryCommand.scala  |  48 +-
 .../commands/events/VisorEventsCommand.scala    | 298 +++++-----
 .../visor/commands/gc/VisorGcCommand.scala      | 111 ++--
 .../visor/commands/kill/VisorKillCommand.scala  |  53 +-
 .../visor/commands/node/VisorNodeCommand.scala  |  47 +-
 .../visor/commands/ping/VisorPingCommand.scala  |  41 +-
 .../commands/start/VisorStartCommand.scala      |  34 +-
 .../commands/tasks/VisorTasksCommand.scala      |  38 +-
 .../commands/top/VisorTopologyCommand.scala     |  36 +-
 .../visor/commands/vvm/VisorVvmCommand.scala    |  32 +-
 .../scala/org/apache/ignite/visor/visor.scala   | 106 ++--
 .../ignite/visor/VisorTextTableSpec.scala       |   3 +-
 28 files changed, 1228 insertions(+), 1501 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeEventsCollectorTask.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeEventsCollectorTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeEventsCollectorTask.java
index ef4415a..a5f64d9 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeEventsCollectorTask.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeEventsCollectorTask.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.visor.node;
 
-import org.apache.ignite.cluster.*;
 import org.apache.ignite.compute.*;
 import org.apache.ignite.events.*;
 import org.apache.ignite.internal.processors.task.*;
@@ -172,7 +171,7 @@ public class VisorNodeEventsCollectorTask extends 
VisorMultiNodeTask<VisorNodeEv
     /**
      * Job for task returns events data.
      */
-    private static class VisorNodeEventsCollectorJob extends 
VisorJob<VisorNodeEventsCollectorTaskArg,
+    protected static class VisorNodeEventsCollectorJob extends 
VisorJob<VisorNodeEventsCollectorTaskArg,
         Collection<? extends VisorGridEvent>> {
         /** */
         private static final long serialVersionUID = 0L;
@@ -183,7 +182,7 @@ public class VisorNodeEventsCollectorTask extends 
VisorMultiNodeTask<VisorNodeEv
          * @param arg Job argument.
          * @param debug Debug flag.
          */
-        private VisorNodeEventsCollectorJob(VisorNodeEventsCollectorTaskArg 
arg, boolean debug) {
+        protected VisorNodeEventsCollectorJob(VisorNodeEventsCollectorTaskArg 
arg, boolean debug) {
             super(arg, debug);
         }
 
@@ -260,6 +259,10 @@ public class VisorNodeEventsCollectorTask extends 
VisorMultiNodeTask<VisorNodeEv
             return true;
         }
 
+        protected IgniteClosure<Event, VisorGridEvent> eventMapper() {
+            return EVT_MAPPER;
+        }
+
         /** {@inheritDoc} */
         @Override protected Collection<? extends VisorGridEvent> run(final 
VisorNodeEventsCollectorTaskArg arg) {
             final long startEvtTime = arg.timeArgument() == null ? 0L : 
System.currentTimeMillis() - arg.timeArgument();
@@ -286,46 +289,19 @@ public class VisorNodeEventsCollectorTask extends 
VisorMultiNodeTask<VisorNodeEv
 
             Long maxOrder = startEvtOrder;
 
-            for (Event e : evts) {
-                int tid = e.type();
-                IgniteUuid id = e.id();
-                String name = e.name();
-                UUID nid = e.node().id();
-                long t = e.timestamp();
-                String msg = e.message();
-                String shortDisplay = e.shortDisplay();
+            IgniteClosure<Event, VisorGridEvent> mapper = eventMapper();
 
+            for (Event e : evts) {
                 maxOrder = Math.max(maxOrder, e.localOrder());
 
-                if (e instanceof TaskEvent) {
-                    TaskEvent te = (TaskEvent)e;
+                VisorGridEvent visorEvt = mapper.apply(e);
 
-                    res.add(new VisorGridTaskEvent(tid, id, name, nid, t, msg, 
shortDisplay,
-                        te.taskName(), te.taskClassName(), te.taskSessionId(), 
te.internal()));
-                }
-                else if (e instanceof JobEvent) {
-                    JobEvent je = (JobEvent)e;
-
-                    res.add(new VisorGridJobEvent(tid, id, name, nid, t, msg, 
shortDisplay,
-                        je.taskName(), je.taskClassName(), je.taskSessionId(), 
je.jobId()));
-                }
-                else if (e instanceof DeploymentEvent) {
-                    DeploymentEvent de = (DeploymentEvent)e;
-
-                    res.add(new VisorGridDeploymentEvent(tid, id, name, nid, 
t, msg, shortDisplay, de.alias()));
-                }
-                else if (e instanceof DiscoveryEvent) {
-                    DiscoveryEvent de = (DiscoveryEvent)e;
-
-                    ClusterNode node = de.eventNode();
-
-                    String addr = F.first(node.addresses());
-
-                    res.add(new VisorGridDiscoveryEvent(tid, id, name, nid, t, 
msg, shortDisplay,
-                        node.id(), addr, node.isDaemon()));
-                }
+                if (visorEvt != null)
+                    res.add(visorEvt);
                 else
-                    res.add(new VisorGridEvent(tid, id, name, nid, t, msg, 
shortDisplay));
+                    res.add(new VisorGridEvent(
+                        e.type(), e.id(), e.name(), e.node().id(), 
e.timestamp(), e.message(), e.shortDisplay()
+                    ));
             }
 
             // Update latest order in node local, if not empty.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorEventMapper.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorEventMapper.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorEventMapper.java
index 3cc7035..b9ff1aa 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorEventMapper.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorEventMapper.java
@@ -17,7 +17,9 @@
 
 package org.apache.ignite.internal.visor.util;
 
+import org.apache.ignite.cluster.*;
 import org.apache.ignite.events.*;
+import org.apache.ignite.internal.util.typedef.*;
 import org.apache.ignite.internal.visor.event.*;
 import org.apache.ignite.lang.*;
 
@@ -65,6 +67,17 @@ public class VisorEventMapper implements 
IgniteClosure<Event, VisorGridEvent> {
             return new VisorGridDeploymentEvent(type, id, name, nid, ts, msg, 
shortDisplay, de.alias());
         }
 
+        if (evt instanceof DiscoveryEvent) {
+            DiscoveryEvent de = (DiscoveryEvent)evt;
+
+            ClusterNode node = de.eventNode();
+
+            String addr = F.first(node.addresses());
+
+            return new VisorGridDiscoveryEvent(type, id, name, nid, ts, msg, 
shortDisplay,
+                node.id(), addr, node.isDaemon());
+        }
+
         return null;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
index 450b1da..7cfc18f 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
@@ -331,7 +331,7 @@ public class VisorTaskUtils {
     };
 
     /** Mapper from grid event to Visor data transfer object. */
-    private static final VisorEventMapper EVT_MAPPER = new VisorEventMapper();
+    public static final VisorEventMapper EVT_MAPPER = new VisorEventMapper();
 
     /**
      * Grabs local events and detects if events was lost since last poll.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala
index bc815b3..daca942 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala
@@ -17,10 +17,13 @@
 
 package org.apache.ignite.visor.commands
 
+import org.apache.ignite.IgniteSystemProperties._
 import org.apache.ignite.internal.IgniteVersionUtils._
 import org.apache.ignite.internal.util.scala.impl
 import org.apache.ignite.internal.util.{IgniteUtils => U}
 import org.apache.ignite.startup.cmdline.AboutDialog
+import org.apache.ignite.visor.visor
+import org.apache.ignite.visor.visor._
 
 import jline.TerminalSupport
 import jline.console.ConsoleReader
@@ -35,201 +38,181 @@ import java.text.SimpleDateFormat
 
 import scala.io._
 
-// Built-in commands.
-// Note the importing of implicit conversions.
-import org.apache.ignite.visor.commands.ack.VisorAckCommand
-import org.apache.ignite.visor.commands.alert.VisorAlertCommand
-import org.apache.ignite.visor.commands.cache.{VisorCacheClearCommand, 
VisorCacheCommand, VisorCacheSwapCommand}
-import org.apache.ignite.visor.commands.config.VisorConfigurationCommand
-import org.apache.ignite.visor.commands.deploy.VisorDeployCommand
-import org.apache.ignite.visor.commands.disco.VisorDiscoveryCommand
-import org.apache.ignite.visor.commands.events.VisorEventsCommand
-import org.apache.ignite.visor.commands.gc.VisorGcCommand
-import org.apache.ignite.visor.commands.kill.VisorKillCommand
-import org.apache.ignite.visor.commands.node.VisorNodeCommand
-import org.apache.ignite.visor.commands.ping.VisorPingCommand
-import org.apache.ignite.visor.commands.start.VisorStartCommand
-import org.apache.ignite.visor.commands.tasks.VisorTasksCommand
-import org.apache.ignite.visor.commands.top.VisorTopologyCommand
-import org.apache.ignite.visor.commands.vvm.VisorVvmCommand
-import org.apache.ignite.visor.visor
-import org.apache.ignite.visor.visor._
-
 /**
  * Command line Visor.
  */
-object VisorConsole extends App {
+class VisorConsole {
     /** Version number. */
-    private final val VISOR_VER = VER_STR
-
-    /** Release date. */
-    private final val VISOR_RELEASE_DATE = RELEASE_DATE_STR
+    protected def version() = VER_STR
 
     /** Copyright. */
-    private final val VISOR_COPYRIGHT = COPYRIGHT
-
-    /** Release date (another format). */
-    private final val releaseDate = new 
SimpleDateFormat("ddMMyyyy").parse(VISOR_RELEASE_DATE)
-
-    // Pre-initialize built-in commands.
-    VisorAckCommand
-    VisorAlertCommand
-    VisorCacheCommand
-    VisorCacheClearCommand
-    VisorCacheSwapCommand
-    VisorConfigurationCommand
-    VisorDeployCommand
-    VisorDiscoveryCommand
-    VisorEventsCommand
-    VisorGcCommand
-    VisorKillCommand
-    VisorNodeCommand
-    VisorPingCommand
-    VisorTopologyCommand
-    VisorStartCommand
-    VisorTasksCommand
-    VisorVvmCommand
-
-    // Setting up Mac OS specific system menu.
-    customizeUI()
-
-    // Wrap line symbol for user input.
-    private val wrapLine = if (U.isWindows) "^" else "\\"
-
-    private val emptyArg = "^([a-zA-z!?]+)$".r
-    private val varArg = "^([a-zA-z!?]+)\\s+(.+)$".r
+    protected def copyright() = COPYRIGHT
 
-    private var line: String = null
+    /** Release date. */
+    protected def releaseDate() = new 
SimpleDateFormat("ddMMyyyy").parse(RELEASE_DATE_STR)
 
-    private val buf = new StringBuilder
+    /** Program name. */
+    protected val progName = sys.props.getOrElse(IGNITE_PROG_NAME, 
"ignitevisorcmd")
 
-    line = args.mkString(" ")
+    /**
+     * Built-in commands.
+     */
+    protected def addCommands() {
+        // Note the importing of implicit conversions.
+        org.apache.ignite.visor.commands.ack.VisorAckCommand
+        org.apache.ignite.visor.commands.alert.VisorAlertCommand
+        org.apache.ignite.visor.commands.cache.VisorCacheClearCommand
+        org.apache.ignite.visor.commands.cache.VisorCacheCommand
+        org.apache.ignite.visor.commands.cache.VisorCacheSwapCommand
+        org.apache.ignite.visor.commands.config.VisorConfigurationCommand
+        org.apache.ignite.visor.commands.deploy.VisorDeployCommand
+        org.apache.ignite.visor.commands.disco.VisorDiscoveryCommand
+        org.apache.ignite.visor.commands.events.VisorEventsCommand
+        org.apache.ignite.visor.commands.gc.VisorGcCommand
+        org.apache.ignite.visor.commands.kill.VisorKillCommand
+        org.apache.ignite.visor.commands.node.VisorNodeCommand
+        org.apache.ignite.visor.commands.ping.VisorPingCommand
+        org.apache.ignite.visor.commands.start.VisorStartCommand
+        org.apache.ignite.visor.commands.tasks.VisorTasksCommand
+        org.apache.ignite.visor.commands.top.VisorTopologyCommand
+        org.apache.ignite.visor.commands.vvm.VisorVvmCommand
+    }
 
-    val argLst = parseArgs(args.mkString(" "))
+    protected def parse(args: String) = {
+        val argLst = parseArgs(args)
 
-    val batchFile = argValue("b", argLst)
-    val batchCommand = argValue("e", argLst)
+        if (hasArgFlag("?", argLst) || hasArgFlag("help", argLst)) {
+            println("Usage:")
+            println(s"    $progName [?]|[{-v}{-np}]|[{-b=<batch commands file 
path>} {-e=command1;command2}]")
+            println("    Where:")
+            println("        ?, /help, -help - show this message.")
+            println("        -v              - verbose mode (quiet by 
default).")
+            println("        -np             - no pause on exit (pause by 
default)")
+            println("        -b              - batch mode with file)")
+            println("        -e              - batch mode with commands)")
 
-    if (batchFile.isDefined && batchCommand.isDefined) {
-        visor.warn(
-            "Illegal options can't contains both command file and commands",
-            "Usage: ignitevisorcmd {-b=<batch commands file path>} 
{-e=command1;command2}"
-        )
+            visor.quit()
+        }
 
-        visor.quit()
+        argLst
     }
 
-    var batchStream: Option[String] = None
-
-    batchFile.foreach(name => {
-        val f = U.resolveIgnitePath(name)
+    protected def buildReader(argLst: ArgList) = {
+        val batchFile = argValue("b", argLst)
+        val batchCommand = argValue("e", argLst)
 
-        if (f == null) {
+        if (batchFile.isDefined && batchCommand.isDefined) {
             visor.warn(
-                "Can't find batch commands file: " + name,
-                "Usage: ignitevisorcmd {-b=<batch command file path>} 
{-e=command1;command2}"
+                "Illegal options can't contains both command file and 
commands",
+                s"Usage: $progName {-b=<batch commands file path>} 
{-e=command1;command2}"
             )
 
             visor.quit()
         }
 
-        batchStream = Some(Source.fromFile(f).getLines().mkString("\n"))
-    })
+        var batchStream: Option[String] = None
 
-    batchCommand.foreach(commands => batchStream = 
Some(commands.replaceAll(";", "\n")))
+        batchFile.foreach(name => {
+            val f = U.resolveIgnitePath(name)
 
-    val inputStream = batchStream match {
-        case Some(cmd) => new ByteArrayInputStream((cmd + 
"\nquit\n").getBytes("UTF-8"))
-        case None => new FileInputStream(FileDescriptor.in)
-    }
+            if (f == null) {
+                visor.warn(
+                    "Can't find batch commands file: " + name,
+                    s"Usage: $progName {-b=<batch command file path>} 
{-e=command1;command2}"
+                )
 
-    // Workaround for IDEA terminal.
-    val term = try {
-        Class.forName("com.intellij.rt.execution.application.AppMain")
+                visor.quit()
+            }
 
-        new TerminalSupport(true) {}
-    } catch {
-        case ignored: ClassNotFoundException => null
-    }
+            batchStream = Some(Source.fromFile(f).getLines().mkString("\n"))
+        })
 
-    private val reader = new ConsoleReader(inputStream, System.out, term)
+        batchCommand.foreach(commands => batchStream = 
Some(commands.replaceAll(";", "\n")))
 
-    reader.addCompleter(new VisorCommandCompleter(visor.commands))
-    reader.addCompleter(new VisorFileNameCompleter())
+        val inputStream = batchStream match {
+            case Some(cmd) => new ByteArrayInputStream((cmd + 
"\nquit\n").getBytes("UTF-8"))
+            case None => new FileInputStream(FileDescriptor.in)
+        }
 
-    private def isHelp(arg: String): Boolean = {
-        val s = arg.trim.toLowerCase
+        // Workaround for IDEA terminal.
+        val term = try {
+            Class.forName("com.intellij.rt.execution.application.AppMain")
 
-        "?" == s || s.endsWith("help")
-    }
+            new TerminalSupport(false) {}
+        } catch {
+            case ignored: ClassNotFoundException => null
+        }
+
+        val reader = new ConsoleReader(inputStream, System.out, term)
+
+        reader.addCompleter(new VisorCommandCompleter(visor.commands))
+        reader.addCompleter(new VisorFileNameCompleter())
 
-    if (args.length > 0 && isHelp(args(0))) {
-        println("Usage:")
-        println("    ignitevisorcmd [?]|[{-v}{-np}]|[{-b=<batch commands file 
path>} {-e=command1;command2}]")
-        println("    Where:")
-        println("        ?, /help, -help - show this message.")
-        println("        -v              - verbose mode (quiet by default).")
-        println("        -np             - no pause on exit (pause by 
default)")
-        println("        -b              - batch mode with file)")
-        println("        -e              - batch mode with commands)")
-
-        System.exit(0)
+        reader
     }
 
-    welcomeMessage()
+    protected def mainLoop(reader: ConsoleReader) {
+        welcomeMessage()
 
-    private var ok = true
+        var ok = true
 
-    while (ok) {
-        line = reader.readLine("visor> ")
+        // Wrap line symbol for user input.
+        val wrapLine = if (U.isWindows) "^" else "\\"
 
-        ok = line != null
+        val emptyArg = "^([a-zA-z!?]+)$".r
+        val varArg = "^([a-zA-z!?]+)\\s+(.+)$".r
 
-        if (ok) {
-            line = line.trim
+        var line: String = null
 
-            if (line.endsWith(wrapLine)) {
-                buf.append(line.dropRight(1))
-            }
-            else {
-                if (buf.size != 0) {
-                    buf.append(line)
+        val buf = new StringBuilder
+
+        while (ok) {
+            line = reader.readLine("visor> ")
+
+            ok = line != null
 
-                    line = buf.toString()
+            if (ok) {
+                line = line.trim
 
-                    buf.clear()
+                if (line.endsWith(wrapLine)) {
+                    buf.append(line.dropRight(1))
                 }
+                else {
+                    if (buf.size != 0) {
+                        buf.append(line)
+
+                        line = buf.toString()
+
+                        buf.clear()
+                    }
 
-                try {
-                    line match {
-                        case emptyArg(c) =>
-                            visor.searchCmd(c) match {
-                                case Some(cmdHolder) => cmdHolder.impl.invoke()
-                                case _ => adviseToHelp(c)
-                            }
-                        case varArg(c, args) =>
-                            visor.searchCmd(c) match {
-                                case Some(cmdHolder) => 
cmdHolder.impl.invoke(args.trim)
-                                case _ => adviseToHelp(c)
-                            }
-                        case s if "".equals(s.trim) => // Ignore empty user 
input.
-                        case _ => adviseToHelp(line)
+                    try {
+                        line match {
+                            case emptyArg(c) =>
+                                visor.searchCmd(c) match {
+                                    case Some(cmdHolder) => 
cmdHolder.emptyArgs()
+                                    case _ => adviseToHelp(c)
+                                }
+                            case varArg(c, a) =>
+                                visor.searchCmd(c) match {
+                                    case Some(cmdHolder) => 
cmdHolder.withArgs(a.trim)
+                                    case _ => adviseToHelp(c)
+                                }
+                            case s if "".equals(s.trim) => // Ignore empty 
user input.
+                            case _ => adviseToHelp(line)
+                        }
+                    } catch {
+                        case ignore: Exception => adviseToHelp(line)
                     }
-                } catch {
-                    case ignore: Exception => adviseToHelp(line)
                 }
             }
         }
     }
 
-    def terminalWidth() = reader.getTerminal.getWidth
-
-    def consoleReader() = reader
-
     /**
      * Prints standard 'Invalid command' error message.
      */
-    private def adviseToHelp(input: String) {
+    protected def adviseToHelp(input: String) {
         visor.warn(
             "Invalid command name: '" + input + "'",
             "Type 'help' to print commands list."
@@ -239,20 +222,31 @@ object VisorConsole extends App {
     /**
      * Print banner, hint message on start.
      */
-    private def welcomeMessage() {
-        visor.status()
+    protected def welcomeMessage() {
+        println("___    _________________________ ________" +  NL +
+                "__ |  / /____  _/__  ___/__  __ \\___  __ \\" +  NL +
+                "__ | / /  __  /  _____ \\ _  / / /__  /_/ /" +  NL +
+                "__ |/ /  __/ /   ____/ / / /_/ / _  _, _/" +  NL +
+                "_____/   /___/   /____/  \\____/  /_/ |_|" +  NL +
+                NL +
+                "ADMIN CONSOLE" + NL +
+                copyright())
+
+        nl()
+
+        status()
 
         println("\nType 'help' for more information.")
         println("Type 'open' to join the grid.")
         println("Type 'quit' to quit form Visor console.")
 
-        visor.nl()
+        nl()
     }
 
     /**
-     * Setting up mac os specific menu.
+     * Setting up Mac OS specific system menu.
      */
-    private def customizeUI() {
+    protected def addAboutDialog() {
         def urlIcon(iconName: String) = {
             val iconPath = "org/apache/ignite/startup/cmdline/" + iconName
 
@@ -281,7 +275,7 @@ object VisorConsole extends App {
                 new java.lang.reflect.InvocationHandler {
                     def invoke(proxy: Any, mth: java.lang.reflect.Method, 
args: Array[Object]) = {
                         AboutDialog.centerShow("Visor - Ignite Shell Console", 
bannerIconUrl.toExternalForm,
-                            VISOR_VER, releaseDate, VISOR_COPYRIGHT)
+                            version(), releaseDate(), copyright())
 
                         null
                     }
@@ -296,6 +290,20 @@ object VisorConsole extends App {
     }
 }
 
+object VisorConsole extends VisorConsole with App {
+    addAboutDialog()
+
+    addCommands()
+
+    private val argLst = parse(args.mkString(" "))
+    
+    private val reader = buildReader(argLst)
+
+    scala.Console.withIn(reader.getInput) {
+        mainLoop(reader)
+    }
+}
+
 /**
  * Visor command list completer.
  *

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsoleCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsoleCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsoleCommand.scala
deleted file mode 100644
index 7192a87..0000000
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsoleCommand.scala
+++ /dev/null
@@ -1,77 +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.visor.commands
-
-import org.apache.ignite.internal.util.scala.impl
-import org.apache.ignite.visor.visor
-
-/**
- * Command implementation.
- */
-trait VisorConsoleCommand {
-    /**
-     * Command without arguments.
-     */
-    def invoke()
-
-    /**
-     * Command with arguments.
-     *
-     * @param args - arguments as string.
-     */
-    def invoke(args: String)
-}
-
-/**
- * Singleton companion object.
- */
-object VisorConsoleCommand {
-    /**
-     * Create `VisorConsoleCommand`.
-     *
-     * @param emptyArgs Function to execute in case of no args passed to 
command.
-     * @param withArgs Function to execute in case of some args passed to 
command.
-     * @return New instance of `VisorConsoleCommand`.
-     */
-    def apply(emptyArgs: () => Unit, withArgs: (String) => Unit) = {
-        new VisorConsoleCommand {
-            @impl def invoke() = emptyArgs.apply()
-
-            @impl def invoke(args: String) = withArgs.apply(args)
-        }
-    }
-
-    /**
-     * Create `VisorConsoleCommand`.
-     *
-     * @param emptyArgs Function to execute in case of no args passed to 
command.
-     * @return New instance of `VisorConsoleCommand`.
-     */
-    def apply(emptyArgs: () => Unit) = {
-        new VisorConsoleCommand {
-            @impl def invoke() = emptyArgs.apply()
-
-            @impl def invoke(args: String) {
-                visor.warn(
-                    "Invalid arguments for command without arguments.",
-                    "Type 'help' to print commands list."
-                )
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorTextTable.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorTextTable.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorTextTable.scala
deleted file mode 100644
index 5cb40b9..0000000
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorTextTable.scala
+++ /dev/null
@@ -1,541 +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.visor.commands
-
-import org.apache.ignite.internal.util.GridStringBuilder
-import org.apache.ignite.visor.commands.VisorTextTable._
-
-import scala.collection.Traversable
-
-/**
- * ==Overview==
- * Provides `ASCII`-based table with minimal styling support.
- */
-class VisorTextTable {
-    /**
-     * Cell style.
-     */
-    private sealed class Style(
-        var leftPad: Int = 1, // >= 0
-        var rightPad: Int = 1, // >= 0
-        var align: String = "center" // center, left, right
-    ) {
-        assert(leftPad >= 0)
-        assert(rightPad >= 0)
-        assert(align != null)
-
-        /**
-         * Gets overall padding (left + right).
-         */
-        def padding: Int =
-            leftPad + rightPad
-    }
-
-    /**
-     * Cell style.
-     */
-    private object Style {
-        /**
-         *
-         * @param sty Style.
-         */
-        def apply(sty: String): Style = {
-            assert(sty != null)
-
-            val cs = new Style
-
-            if (!sty.isEmpty) {
-                for (e <- sty.split(',')) {
-                    val a = e.split(":")
-
-                    assert(a.length == 2, "Invalid cell style: " + e.trim)
-
-                    val a0 = a(0).trim
-                    val a1 = a(1).trim
-
-                    a0 match {
-                        case "leftPad" => cs.leftPad = a1.toInt
-                        case "rightPad" => cs.rightPad = a1.toInt
-                        case "align" => cs.align = a1
-                        case _ => assert(false, "Invalid style: " + e.trim)
-                    }
-                }
-            }
-
-            cs
-        }
-    }
-
-    /**
-     * Cell holder.
-     */
-    private sealed case class Cell(style: Style, lines: Seq[String]) {
-        assert(style != null)
-        assert(lines != null)
-
-        /**
-         * Cell's calculated width including padding.
-         */
-        lazy val width =
-            if (height > 0)
-                style.padding + lines.max(Ordering.by[String, 
Int](_.length)).length
-            else
-                style.padding
-
-        /**
-         * Gets height of the cell.
-         */
-        def height: Int = lines.length
-    }
-
-    /**
-     * Margin holder.
-     */
-    private sealed case class Margin(
-        top: Int = 0,
-        right: Int = 0,
-        bottom: Int = 0,
-        left: Int = 0) {
-        assert(top >= 0)
-        assert(right >= 0)
-        assert(bottom >= 0)
-        assert(left >= 0)
-    }
-
-    /** */
-    private val NL = '\n'
-
-    /** Headers. */
-    private val hdr = collection.mutable.ArrayBuffer.empty[Cell]
-
-    /** Rows. */
-    private val rows = collection.mutable.ArrayBuffer.empty[Seq[Cell]]
-
-    /** Current row, if any. */
-    private var curRow: collection.mutable.ArrayBuffer[Cell] = null
-
-    /** Table's margin, if any. */
-    private var margin: Margin = Margin()
-
-    /** Default row cell style, if any. */
-    private var rowSty: String = "align:left"
-
-    /** Default header cell style, if any. */
-    private var hdrSty: String = "align:center"
-
-    /**
-     * Flag indicating whether or not to draw inside horizontal lines
-     * between individual rows.
-     */
-    var insideBorder = false
-
-    /**
-     * Flag indicating whether of not to automatically draw horizontal lines
-     * for multiline rows.
-     */
-    var autoBorder = true
-
-    /**
-     * Maximum width of the cell. If any line in the cell exceeds this width
-     * it will be cut in two or more lines.
-     *
-     * '''NOTE''': it doesn't include into account the padding. Only the actual
-     * string length is counted.
-     */
-    var maxCellWidth = Int.MaxValue
-
-    /**
-     *
-     * @param ch Char.
-     * @param len Dash length.
-     */
-    private def dash(ch: Char, len: Int): String = {
-        assert(len >= 0)
-
-        new String().padTo(len, ch)
-    }
-
-    /**
-     *
-     * @param s String.
-     * @param len Dash length.
-     */
-    private def dash(s: String, len: Int): String = {
-        assert(len >= 0)
-
-        var i = 0
-
-        val sb = new GridStringBuilder(s.length * len)
-
-        while (i < len) {
-            sb.a(s)
-
-            i += 1
-        }
-
-        sb.toString
-    }
-
-    /**
-     *
-     * @param len Length.
-     */
-    private def blank(len: Int): String =
-        dash(' ', len)
-
-    /**
-     * Sets table's margin.
-     *
-     * @param top Top margin.
-     * @param right Right margin.
-     * @param bottom Bottom margin.
-     * @param left Left margin.
-     */
-    def margin(top: Int = 0, right: Int = 0, bottom: Int = 0, left: Int = 0) {
-        assert(top >= 0)
-        assert(right >= 0)
-        assert(bottom >= 0)
-        assert(left >= 0)
-
-        margin = Margin(top, right, bottom, left)
-    }
-
-    /**
-     * Starts data row.
-     */
-    def startRow() {
-        assert(curRow == null)
-
-        curRow = collection.mutable.ArrayBuffer.empty[Cell]
-    }
-
-    /**
-     * Ends data row.
-     */
-    def endRow() {
-        assert(curRow.nonEmpty)
-
-        rows += curRow
-
-        curRow = null
-    }
-
-    /**
-     * Adds row (one or more row cells).
-     *
-     * @param cells Row cells. For multi-line cells - use `Seq(...)`.
-     */
-    def +=(cells: Any*): VisorTextTable = {
-        startRow()
-
-        cells foreach {
-            case s: scala.collection.Iterable[Any] => addRowCell(s.toSeq: _*)
-            case p: Product => addRowCell(p.productIterator.toSeq: _*)
-            case a => addRowCell(a)
-        }
-
-        endRow()
-
-        this
-    }
-
-    /**
-     * Adds header (one or more header cells).
-     *
-     * @param cells Header cells. For multi-line cells - use `Seq(...)`.
-     */
-    def #=(cells: Any*): VisorTextTable = {
-        cells foreach {
-            case s: scala.collection.Iterable[Any] => addHeaderCell(s.toSeq: 
_*)
-            case p: Product => addHeaderCell(p.productIterator.toSeq: _*)
-            case a => addHeaderCell(a)
-        }
-
-        this
-    }
-
-    /**
-     * Adds single header cell.
-     *
-     * @param lines One or more cell lines.
-     */
-    def addHeaderCell(lines: Any*): VisorTextTable = {
-        assert(lines != null)
-        assert(lines.length > 0)
-
-        // Break up long line into multiple ones - if necessary.
-        val lst = lines flatten(_.toString.grouped(maxCellWidth))
-
-        hdr += Cell(Style(hdrSty), lst)
-
-        this
-    }
-
-    /**
-     * Gets current row style.
-     */
-    def rowStyle =
-        rowSty
-
-    /**
-     * Sets current row style.
-     *
-     * @param rowSty Row style to set.
-     */
-    def rowStyle(rowSty: String) {
-        this.rowSty = rowSty
-    }
-
-    /**
-     * Gets current header style.
-     */
-    def headerStyle =
-        rowSty
-
-    /**
-     * Sets current header style.
-     *
-     * @param hdrSty Header style to set.
-     */
-    def headerStyle(hdrSty: String) {
-        this.hdrSty = hdrSty
-    }
-
-    /**
-     * Adds single row cell.
-     *
-     * @param lines One or more row cells. Multiple lines will be printed on 
separate lines.
-     */
-    def addRowCell(lines: Any*): VisorTextTable = {
-        assert(lines != null)
-        assert(lines.length >= 0)
-        assert(curRow != null)
-
-        // Break up long line into multiple ones - if necessary.
-        val lst = lines flatten {
-            case it: Traversable[_] => 
it.flatten(_.toString.grouped(maxCellWidth))
-            case null => Seq("")
-            case obj => obj.toString.grouped(maxCellWidth)
-        }
-
-        curRow += Cell(Style(rowSty), lst)
-
-        this
-    }
-
-    /**
-     *
-     * @param txt Text to align.
-     * @param width Width already accounts for padding.
-     * @param sty Style.
-     */
-    private def aligned(txt: String, width: Int, sty: Style): String = {
-        assert(txt != null)
-        assert(width > 0)
-        assert(sty != null)
-        assert(txt.length <= width)
-
-        val d = width - txt.length
-
-        val styTxt = txt
-
-        sty.align.trim match {
-            case "center" =>
-                blank(d / 2) + styTxt + blank(d / 2 + d % 2)
-            case "left" =>
-                blank(sty.leftPad) + styTxt + blank(d - sty.leftPad)
-            case "right" =>
-                blank(d - sty.rightPad) + styTxt + blank(sty.rightPad)
-            case _ =>
-                throw new AssertionError("Invalid align option in: " + sty)
-        }
-    }
-
-    /**
-     * Renders this table.
-     */
-    def render() {
-        // Make sure table is not empty.
-        if (hdr.isEmpty && rows.isEmpty)
-            return
-
-        var colsNum = -1
-
-        val isHdr = hdr.nonEmpty
-
-        if (isHdr)
-            colsNum = hdr.size
-
-        // Calc number of columns and make sure all rows are even.
-        for (r <- rows)
-            if (colsNum == -1)
-                colsNum = r.size
-            else if (colsNum != r.size)
-                assert(false, "Table with uneven rows.")
-
-        assert(colsNum > 0)
-
-        // At this point all rows in the table have the
-        // the same number of columns.
-
-        val colWs = new Array[Int](colsNum) // Column widths.
-        val rowHs = new Array[Int](rows.length) // Row heights.
-
-        // Header height.
-        var hdrH = 0
-
-        // Initialize column widths with header row (if any).
-        for (i <- 0 until hdr.size) {
-            val c = hdr(i)
-
-            colWs(i) = c.width
-
-            hdrH = math.max(hdrH, c.height)
-        }
-
-        // Calc row heights and column widths.
-        for (i <- 0 until rows.length; j <- 0 until colsNum) {
-            val c = rows(i)(j)
-
-            rowHs(i) = math.max(rowHs(i), c.height)
-            colWs(j) = math.max(colWs(j), c.width)
-        }
-
-        // Table width without the border.
-        val tblW = colWs.sum + colsNum - 1
-
-        val tbl = new GridStringBuilder()
-
-        // Top margin.
-        for (i <- 0 until margin.top)
-            tbl.a(" ").a(NL)
-
-        // Print header, if any.
-        if (isHdr) {
-            tbl.a(blank(margin.left)).a(HDR_CRS).a(dash(HDR_HOR, 
tblW)).a(HDR_CRS).a(blank(margin.right)).a(NL)
-
-            for (i <- 0 until hdrH) {
-                // Left margin and '|'.
-                tbl.a(blank(margin.left)).a(HDR_VER)
-
-                for (j <- 0 until hdr.size) {
-                    val c = hdr(j)
-
-                    if (i >= 0 && i < c.height)
-                        tbl.a(aligned(c.lines(i), colWs(j), c.style))
-                    else
-                        tbl.a(blank(colWs(j)))
-
-                    tbl.a(HDR_VER) // '|'
-                }
-
-                // Right margin.
-                tbl.a(blank(margin.right)).a(NL)
-            }
-
-            tbl.a(blank(margin.left)).a(HDR_CRS).a(dash(HDR_HOR, 
tblW)).a(HDR_CRS).a(blank(margin.right)).a(NL)
-        }
-        else
-            tbl.a(blank(margin.left)).a(ROW_CRS).a(dash(ROW_HOR, 
tblW)).a(ROW_CRS).a(blank(margin.right)).a(NL)
-
-        // Print rows, if any.
-        if (rows.nonEmpty) {
-            val horLine = (i: Int) => {
-                // Left margin and '+'
-                tbl.a(blank(margin.left)).a(ROW_CRS)
-
-                for (k <- 0 until rows(i).size)
-                    tbl.a(dash(ROW_HOR, colWs(k))).a(ROW_CRS)
-
-                // Right margin.
-                tbl.a(blank(margin.right)).a(NL)
-            }
-
-            for (i <- 0 until rows.size) {
-                val r = rows(i)
-
-                val rowH = rowHs(i)
-
-                if (i > 0 && ((rowH > 1 && autoBorder) || insideBorder) && 
rowHs(i - 1) == 1)
-                    horLine(i)
-
-                for (j <- 0 until rowH) {
-                    // Left margin and '|'
-                    tbl.a(blank(margin.left)).a(ROW_VER)
-
-                    for (k <- 0 until r.size) {
-                        val c = r(k)
-                        val w = colWs(k)
-
-                        if (j < c.height)
-                            tbl.a(aligned(c.lines(j), w, c.style))
-                        else
-                            tbl.a(blank(w))
-
-                        tbl.a(ROW_VER) // '|'
-                    }
-
-                    // Right margin.
-                    tbl.a(blank(margin.right)).a(NL)
-                }
-
-                if (i < rows.size - 1 && ((rowH > 1 && autoBorder) || 
insideBorder))
-                    horLine(i)
-            }
-
-            tbl.a(blank(margin.left)).a(ROW_CRS).a(dash(ROW_HOR, 
tblW)).a(ROW_CRS).a(blank(margin.right)).a(NL)
-        }
-
-        // Bottom margin.
-        for (i <- 1 to margin.bottom)
-            tbl.a(" ").a(NL)
-
-        print(tbl.toString)
-    }
-
-    def nonEmpty = rows.nonEmpty
-}
-
-/**
- * Static context.
- */
-object VisorTextTable {
-    /** Table header horizontal line. */
-    private val HDR_HOR = "="
-
-    /** Table header vertical line. */
-    private val HDR_VER = "|"
-
-    /** Table header crossroad line. */
-    private val HDR_CRS = "+"
-
-    /** Table row horizontal line. */
-    private val ROW_HOR = '-'
-
-    /** Table row vertical line. */
-    private val ROW_VER = '|'
-
-    /** Table row crossroad line. */
-    private val ROW_CRS = "+"
-
-    /**
-      * Creates new Visor text table.
-      */
-    def apply() =
-        new VisorTextTable
-}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ack/VisorAckCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ack/VisorAckCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ack/VisorAckCommand.scala
index a8e2802..2e3659d 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ack/VisorAckCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ack/VisorAckCommand.scala
@@ -18,8 +18,9 @@
 package org.apache.ignite.visor.commands.ack
 
 import org.apache.ignite.cluster.ClusterGroupEmptyException
+import org.apache.ignite.internal.util.scala.impl
 import org.apache.ignite.visor.VisorTag
-import org.apache.ignite.visor.commands.VisorConsoleCommand
+import org.apache.ignite.visor.commands.common.VisorConsoleCommand
 import org.apache.ignite.visor.visor._
 
 import org.apache.ignite.internal.visor.misc.VisorAckTask
@@ -61,18 +62,8 @@ import scala.language.implicitConversions
  *         Prints local node ID on all nodes in the topology.
  * }}}
  */
-class VisorAckCommand {
-    /**
-     * Prints error message and advise.
-     *
-     * @param errMsgs Error messages.
-     */
-    private def scold(errMsgs: Any*) {
-        assert(errMsgs != null)
-
-        warn(errMsgs: _*)
-        warn("Type 'help ack' to see how to use this command.")
-    }
+class VisorAckCommand extends VisorConsoleCommand {
+    @impl protected val name = "ack"
 
     /**
      * ===Command===
@@ -115,6 +106,9 @@ class VisorAckCommand {
  * Companion object that does initialization of the command.
  */
 object VisorAckCommand {
+    /** Singleton command. */
+    private val cmd = new VisorAckCommand
+
     // Adds command's help to visor.
     addHelp(
         name = "ack",
@@ -133,12 +127,10 @@ object VisorAckCommand {
             "ack Howdy!" ->
                 "Prints 'Howdy!' on all nodes in the topology."
         ),
-        ref = VisorConsoleCommand(cmd.ack, cmd.ack)
+        emptyArgs = cmd.ack,
+        withArgs = cmd.ack
     )
 
-    /** Singleton command. */
-    private val cmd = new VisorAckCommand
-
     /**
      * Singleton.
      */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/alert/VisorAlertCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/alert/VisorAlertCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/alert/VisorAlertCommand.scala
index f94f481..9e2aec0 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/alert/VisorAlertCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/alert/VisorAlertCommand.scala
@@ -21,16 +21,16 @@ import org.apache.ignite._
 import org.apache.ignite.cluster.ClusterNode
 import org.apache.ignite.events.EventType._
 import org.apache.ignite.events.{DiscoveryEvent, Event}
+import org.apache.ignite.internal.util.scala.impl
 import org.apache.ignite.internal.util.{IgniteUtils => U}
 import org.apache.ignite.lang.IgnitePredicate
+import org.apache.ignite.visor.VisorTag
+import org.apache.ignite.visor.commands.common.{VisorConsoleCommand, 
VisorTextTable}
+import org.apache.ignite.visor.visor._
 
 import java.util.UUID
 import java.util.concurrent.atomic._
 
-import org.apache.ignite.visor.VisorTag
-import org.apache.ignite.visor.commands.{VisorConsoleCommand, VisorTextTable}
-import org.apache.ignite.visor.visor._
-
 import scala.collection.immutable._
 import scala.language.implicitConversions
 import scala.util.control.Breaks._
@@ -119,7 +119,9 @@ import scala.util.control.Breaks._
  *         Notify every 15 min if grid has >= 4 CPUs and > 50% CPU load.
  * }}}
  */
-class VisorAlertCommand {
+class VisorAlertCommand extends VisorConsoleCommand {
+    @impl protected val name = "alert"
+
     /** Default alert frequency. */
     val DFLT_FREQ = 15L * 60L
 
@@ -142,18 +144,6 @@ class VisorAlertCommand {
     private var lsnr: IgnitePredicate[Event] = null
 
     /**
-     * Prints error message and advise.
-     *
-     * @param errMsgs Error messages.
-     */
-    private def scold(errMsgs: Any*) {
-        assert(errMsgs != null)
-
-        warn(errMsgs: _*)
-        warn("Type 'help alert' to see how to use this command.")
-    }
-
-    /**
      * ===Command===
      * Lists all registered alerts.
      *
@@ -297,7 +287,7 @@ class VisorAlertCommand {
                         break()
 
                     case e: Exception =>
-                        scold(e.getMessage)
+                        scold(e)
 
                         break()
                 }
@@ -667,6 +657,9 @@ sealed private case class VisorStats(
  * Companion object that does initialization of the command.
  */
 object VisorAlertCommand {
+    /** Singleton command. */
+    private val cmd = new VisorAlertCommand
+
     addHelp(
         name = "alert",
         shortInfo = "Alerts for user-defined events.",
@@ -746,12 +739,10 @@ object VisorAlertCommand {
             "alert -r -t=900 -cc=gte4 -cl=gt50" ->
                 "Notify every 15 min if grid has >= 4 CPUs and > 50% CPU load."
         ),
-        ref = VisorConsoleCommand(cmd.alert, cmd.alert)
+        emptyArgs = cmd.alert,
+        withArgs = cmd.alert
     )
 
-    /** Singleton command. */
-    private val cmd = new VisorAlertCommand
-
     addCloseCallback(() => {
         cmd.reset()
     })

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheClearCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheClearCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheClearCommand.scala
index 2588551..67aaa14 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheClearCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheClearCommand.scala
@@ -17,14 +17,12 @@
 
 package org.apache.ignite.visor.commands.cache
 
-import org.apache.ignite.internal.visor.cache.VisorCacheClearTask
-import org.apache.ignite.internal.visor.util.VisorTaskUtils._
-
 import org.apache.ignite.cluster.{ClusterGroupEmptyException, ClusterNode}
+import org.apache.ignite.visor.commands.common.VisorTextTable
+import org.apache.ignite.visor.visor._
 
-import org.apache.ignite.visor.commands.VisorTextTable
-import org.apache.ignite.visor.visor
-import visor._
+import org.apache.ignite.internal.visor.cache.VisorCacheClearTask
+import org.apache.ignite.internal.visor.util.VisorTaskUtils._
 
 import scala.language.reflectiveCalls
 
@@ -115,7 +113,7 @@ class VisorCacheClearCommand {
         }
         catch {
             case e: ClusterGroupEmptyException => 
scold(messageNodeNotFound(node, cacheName))
-            case e: Throwable =>  scold(e.getMessage)
+            case e: Throwable =>  scold(e)
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
index 2115535..8583637 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala
@@ -23,7 +23,7 @@ import org.apache.ignite.internal.util.typedef._
 import org.apache.ignite.lang.IgniteBiTuple
 import org.apache.ignite.visor.VisorTag
 import org.apache.ignite.visor.commands.cache.VisorCacheCommand._
-import org.apache.ignite.visor.commands.{VisorConsoleCommand, VisorTextTable}
+import org.apache.ignite.visor.commands.common.VisorTextTable
 import org.apache.ignite.visor.visor._
 
 import org.jetbrains.annotations._
@@ -521,7 +521,7 @@ class VisorCacheCommand {
         }
         catch {
             case e: IgniteException =>
-                scold(e.getMessage)
+                scold(e)
 
                 null
         }
@@ -662,6 +662,9 @@ class VisorCacheCommand {
  * Companion object that does initialization of the command.
  */
 object VisorCacheCommand {
+    /** Singleton command */
+    private val cmd = new VisorCacheCommand
+
     addHelp(
         name = "cache",
         shortInfo = "Prints cache statistics, clears cache, prints list of all 
entries from cache.",
@@ -777,7 +780,8 @@ object VisorCacheCommand {
             "cache -swap -c=@c0" -> "Swaps entries in cache with name taken 
from 'c0' memory variable.",
             "cache -stop -c=@c0" -> "Stop cache with name taken from 'c0' 
memory variable."
         ),
-        ref = VisorConsoleCommand(cmd.cache, cmd.cache)
+        emptyArgs = cmd.cache,
+        withArgs = cmd.cache
     )
 
     /** Default cache name to show on screen. */
@@ -786,9 +790,6 @@ object VisorCacheCommand {
     /** Default cache key. */
     protected val DFLT_CACHE_KEY = DFLT_CACHE_NAME + "-" + 
UUID.randomUUID().toString
 
-    /** Singleton command */
-    private val cmd = new VisorCacheCommand
-
     /**
      * Singleton.
      */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheScanCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheScanCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheScanCommand.scala
index 2ff0262..d40ec8d 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheScanCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheScanCommand.scala
@@ -19,7 +19,7 @@ package org.apache.ignite.visor.commands.cache
 
 import org.apache.ignite.cluster.{ClusterGroupEmptyException, ClusterNode}
 import org.apache.ignite.lang.IgniteBiTuple
-import org.apache.ignite.visor.commands._
+import org.apache.ignite.visor.commands.common.VisorTextTable
 import org.apache.ignite.visor.visor._
 
 import org.apache.ignite.internal.visor.query._
@@ -138,9 +138,8 @@ class VisorCacheScanCommand {
 
         val firstPage =
             try
-                val grp = groupForDataNode(node, cacheName)
-
-                executeRandom(grp, classOf[VisorQueryTask], new 
VisorQueryArg(cacheName, "SCAN", pageSize)) match {
+                executeRandom(groupForDataNode(node, cacheName),
+                    classOf[VisorQueryTask], new VisorQueryArg(cacheName, 
"SCAN", pageSize)) match {
                     case x if x.get1() != null =>
                         error(x.get1())
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheSwapCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheSwapCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheSwapCommand.scala
index f43d668..cd01dfe 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheSwapCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheSwapCommand.scala
@@ -18,7 +18,7 @@
 package org.apache.ignite.visor.commands.cache
 
 import org.apache.ignite.cluster.{ClusterGroupEmptyException, ClusterNode}
-import org.apache.ignite.visor.commands.VisorTextTable
+import org.apache.ignite.visor.commands.common.VisorTextTable
 import org.apache.ignite.visor.visor._
 
 import java.util.Collections
@@ -106,7 +106,9 @@ class VisorCacheSwapCommand {
 
             t #=("Node ID8(@)", "Entries Swapped", "Cache Size Before", "Cache 
Size After")
 
-            for (node <- grp.nodes(); nid <- node.id()) {
+            for (node <- grp.nodes()) {
+                val nid = node.id()
+
                 val r = executeOne(nid, classOf[VisorCacheSwapBackupsTask], 
Collections.singleton(cacheName)).
                     get(cacheName)
 
@@ -124,7 +126,7 @@ class VisorCacheSwapCommand {
         }
         catch {
             case e: ClusterGroupEmptyException => 
scold(messageNodeNotFound(node, cacheName))
-            case e: Throwable => scold(e.getMessage)
+            case e: Throwable => scold(e)
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorConsoleCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorConsoleCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorConsoleCommand.scala
new file mode 100644
index 0000000..e20b901
--- /dev/null
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorConsoleCommand.scala
@@ -0,0 +1,66 @@
+/*
+ *
+ *  * 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.visor.commands.common
+
+/**
+ * Command implementation.
+ */
+trait VisorConsoleCommand {
+    protected def name: String
+
+    /**
+     * Prints properly formatted error message like:
+     * {{{
+     * (wrn) <visor>: warning message
+     * }}}
+     *
+     * @param warnMsgs Error messages to print. If `null` - this function is 
no-op.
+     */
+    protected def warn(warnMsgs: Any*) {
+        assert(warnMsgs != null)
+
+        warnMsgs.foreach{
+            case ex: Throwable => println(s"(wrn) <visor>: ${ex.getMessage}")
+            case line => println(s"(wrn) <visor>: $line")
+        }
+    }
+
+    /**
+     * Prints standard 'not connected' warn message.
+     */
+    protected def adviseToConnect() {
+        warn(
+            "Visor is disconnected.",
+            "Type 'open' to connect Visor console or 'help open' to get help."
+        )
+    }
+
+    /**
+     * Prints warn message and advise.
+     *
+     * @param warnMsgs Warning messages.
+     */
+    protected def scold(warnMsgs: Any*) {
+        assert(warnMsgs != null)
+
+        warn(warnMsgs: _*)
+        warn(s"Type 'help $name' to see how to use this command.")
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorTextTable.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorTextTable.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorTextTable.scala
new file mode 100644
index 0000000..e6fe35f
--- /dev/null
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/common/VisorTextTable.scala
@@ -0,0 +1,543 @@
+/*
+ *
+ *  * 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.visor.commands.common
+
+import org.apache.ignite.internal.util.GridStringBuilder
+import org.apache.ignite.visor.commands.common.VisorTextTable._
+
+import scala.collection.Traversable
+
+/**
+ * ==Overview==
+ * Provides `ASCII`-based table with minimal styling support.
+ */
+class VisorTextTable {
+    /**
+     * Cell style.
+     */
+    private sealed class Style(
+        var leftPad: Int = 1, // >= 0
+        var rightPad: Int = 1, // >= 0
+        var align: String = "center" // center, left, right
+    ) {
+        assert(leftPad >= 0)
+        assert(rightPad >= 0)
+        assert(align != null)
+
+        /**
+         * Gets overall padding (left + right).
+         */
+        def padding: Int =
+            leftPad + rightPad
+    }
+
+    /**
+     * Cell style.
+     */
+    private object Style {
+        /**
+         *
+         * @param sty Style.
+         */
+        def apply(sty: String): Style = {
+            assert(sty != null)
+
+            val cs = new Style
+
+            if (!sty.isEmpty) {
+                for (e <- sty.split(',')) {
+                    val a = e.split(":")
+
+                    assert(a.length == 2, "Invalid cell style: " + e.trim)
+
+                    val a0 = a(0).trim
+                    val a1 = a(1).trim
+
+                    a0 match {
+                        case "leftPad" => cs.leftPad = a1.toInt
+                        case "rightPad" => cs.rightPad = a1.toInt
+                        case "align" => cs.align = a1
+                        case _ => assert(false, "Invalid style: " + e.trim)
+                    }
+                }
+            }
+
+            cs
+        }
+    }
+
+    /**
+     * Cell holder.
+     */
+    private sealed case class Cell(style: Style, lines: Seq[String]) {
+        assert(style != null)
+        assert(lines != null)
+
+        /**
+         * Cell's calculated width including padding.
+         */
+        lazy val width =
+            if (height > 0)
+                style.padding + lines.max(Ordering.by[String, 
Int](_.length)).length
+            else
+                style.padding
+
+        /**
+         * Gets height of the cell.
+         */
+        def height: Int = lines.length
+    }
+
+    /**
+     * Margin holder.
+     */
+    private sealed case class Margin(
+        top: Int = 0,
+        right: Int = 0,
+        bottom: Int = 0,
+        left: Int = 0) {
+        assert(top >= 0)
+        assert(right >= 0)
+        assert(bottom >= 0)
+        assert(left >= 0)
+    }
+
+    /** */
+    private val NL = '\n'
+
+    /** Headers. */
+    private val hdr = collection.mutable.ArrayBuffer.empty[Cell]
+
+    /** Rows. */
+    private val rows = collection.mutable.ArrayBuffer.empty[Seq[Cell]]
+
+    /** Current row, if any. */
+    private var curRow: collection.mutable.ArrayBuffer[Cell] = null
+
+    /** Table's margin, if any. */
+    private var margin: Margin = Margin()
+
+    /** Default row cell style, if any. */
+    private var rowSty: String = "align:left"
+
+    /** Default header cell style, if any. */
+    private var hdrSty: String = "align:center"
+
+    /**
+     * Flag indicating whether or not to draw inside horizontal lines
+     * between individual rows.
+     */
+    var insideBorder = false
+
+    /**
+     * Flag indicating whether of not to automatically draw horizontal lines
+     * for multiline rows.
+     */
+    var autoBorder = true
+
+    /**
+     * Maximum width of the cell. If any line in the cell exceeds this width
+     * it will be cut in two or more lines.
+     *
+     * '''NOTE''': it doesn't include into account the padding. Only the actual
+     * string length is counted.
+     */
+    var maxCellWidth = Int.MaxValue
+
+    /**
+     *
+     * @param ch Char.
+     * @param len Dash length.
+     */
+    private def dash(ch: Char, len: Int): String = {
+        assert(len >= 0)
+
+        new String().padTo(len, ch)
+    }
+
+    /**
+     *
+     * @param s String.
+     * @param len Dash length.
+     */
+    private def dash(s: String, len: Int): String = {
+        assert(len >= 0)
+
+        var i = 0
+
+        val sb = new GridStringBuilder(s.length * len)
+
+        while (i < len) {
+            sb.a(s)
+
+            i += 1
+        }
+
+        sb.toString
+    }
+
+    /**
+     *
+     * @param len Length.
+     */
+    private def blank(len: Int): String =
+        dash(' ', len)
+
+    /**
+     * Sets table's margin.
+     *
+     * @param top Top margin.
+     * @param right Right margin.
+     * @param bottom Bottom margin.
+     * @param left Left margin.
+     */
+    def margin(top: Int = 0, right: Int = 0, bottom: Int = 0, left: Int = 0) {
+        assert(top >= 0)
+        assert(right >= 0)
+        assert(bottom >= 0)
+        assert(left >= 0)
+
+        margin = Margin(top, right, bottom, left)
+    }
+
+    /**
+     * Starts data row.
+     */
+    def startRow() {
+        assert(curRow == null)
+
+        curRow = collection.mutable.ArrayBuffer.empty[Cell]
+    }
+
+    /**
+     * Ends data row.
+     */
+    def endRow() {
+        assert(curRow.nonEmpty)
+
+        rows += curRow
+
+        curRow = null
+    }
+
+    /**
+     * Adds row (one or more row cells).
+     *
+     * @param cells Row cells. For multi-line cells - use `Seq(...)`.
+     */
+    def +=(cells: Any*): VisorTextTable = {
+        startRow()
+
+        cells foreach {
+            case s: scala.collection.Iterable[Any] => addRowCell(s.toSeq: _*)
+            case p: Product => addRowCell(p.productIterator.toSeq: _*)
+            case a => addRowCell(a)
+        }
+
+        endRow()
+
+        this
+    }
+
+    /**
+     * Adds header (one or more header cells).
+     *
+     * @param cells Header cells. For multi-line cells - use `Seq(...)`.
+     */
+    def #=(cells: Any*): VisorTextTable = {
+        cells foreach {
+            case s: scala.collection.Iterable[Any] => addHeaderCell(s.toSeq: 
_*)
+            case p: Product => addHeaderCell(p.productIterator.toSeq: _*)
+            case a => addHeaderCell(a)
+        }
+
+        this
+    }
+
+    /**
+     * Adds single header cell.
+     *
+     * @param lines One or more cell lines.
+     */
+    def addHeaderCell(lines: Any*): VisorTextTable = {
+        assert(lines != null)
+        assert(lines.length > 0)
+
+        // Break up long line into multiple ones - if necessary.
+        val lst = lines flatten(_.toString.grouped(maxCellWidth))
+
+        hdr += Cell(Style(hdrSty), lst)
+
+        this
+    }
+
+    /**
+     * Gets current row style.
+     */
+    def rowStyle =
+        rowSty
+
+    /**
+     * Sets current row style.
+     *
+     * @param rowSty Row style to set.
+     */
+    def rowStyle(rowSty: String) {
+        this.rowSty = rowSty
+    }
+
+    /**
+     * Gets current header style.
+     */
+    def headerStyle =
+        rowSty
+
+    /**
+     * Sets current header style.
+     *
+     * @param hdrSty Header style to set.
+     */
+    def headerStyle(hdrSty: String) {
+        this.hdrSty = hdrSty
+    }
+
+    /**
+     * Adds single row cell.
+     *
+     * @param lines One or more row cells. Multiple lines will be printed on 
separate lines.
+     */
+    def addRowCell(lines: Any*): VisorTextTable = {
+        assert(lines != null)
+        assert(lines.length >= 0)
+        assert(curRow != null)
+
+        // Break up long line into multiple ones - if necessary.
+        val lst = lines flatten {
+            case it: Traversable[_] => 
it.flatten(_.toString.grouped(maxCellWidth))
+            case null => Seq("")
+            case obj => obj.toString.grouped(maxCellWidth)
+        }
+
+        curRow += Cell(Style(rowSty), lst)
+
+        this
+    }
+
+    /**
+     *
+     * @param txt Text to align.
+     * @param width Width already accounts for padding.
+     * @param sty Style.
+     */
+    private def aligned(txt: String, width: Int, sty: Style): String = {
+        assert(txt != null)
+        assert(width > 0)
+        assert(sty != null)
+        assert(txt.length <= width)
+
+        val d = width - txt.length
+
+        val styTxt = txt
+
+        sty.align.trim match {
+            case "center" =>
+                blank(d / 2) + styTxt + blank(d / 2 + d % 2)
+            case "left" =>
+                blank(sty.leftPad) + styTxt + blank(d - sty.leftPad)
+            case "right" =>
+                blank(d - sty.rightPad) + styTxt + blank(sty.rightPad)
+            case _ =>
+                throw new AssertionError("Invalid align option in: " + sty)
+        }
+    }
+
+    /**
+     * Renders this table.
+     */
+    def render() {
+        // Make sure table is not empty.
+        if (hdr.isEmpty && rows.isEmpty)
+            return
+
+        var colsNum = -1
+
+        val isHdr = hdr.nonEmpty
+
+        if (isHdr)
+            colsNum = hdr.size
+
+        // Calc number of columns and make sure all rows are even.
+        for (r <- rows)
+            if (colsNum == -1)
+                colsNum = r.size
+            else if (colsNum != r.size)
+                assert(false, "Table with uneven rows.")
+
+        assert(colsNum > 0)
+
+        // At this point all rows in the table have the
+        // the same number of columns.
+
+        val colWs = new Array[Int](colsNum) // Column widths.
+        val rowHs = new Array[Int](rows.length) // Row heights.
+
+        // Header height.
+        var hdrH = 0
+
+        // Initialize column widths with header row (if any).
+        for (i <- 0 until hdr.size) {
+            val c = hdr(i)
+
+            colWs(i) = c.width
+
+            hdrH = math.max(hdrH, c.height)
+        }
+
+        // Calc row heights and column widths.
+        for (i <- 0 until rows.length; j <- 0 until colsNum) {
+            val c = rows(i)(j)
+
+            rowHs(i) = math.max(rowHs(i), c.height)
+            colWs(j) = math.max(colWs(j), c.width)
+        }
+
+        // Table width without the border.
+        val tblW = colWs.sum + colsNum - 1
+
+        val tbl = new GridStringBuilder()
+
+        // Top margin.
+        for (i <- 0 until margin.top)
+            tbl.a(" ").a(NL)
+
+        // Print header, if any.
+        if (isHdr) {
+            tbl.a(blank(margin.left)).a(HDR_CRS).a(dash(HDR_HOR, 
tblW)).a(HDR_CRS).a(blank(margin.right)).a(NL)
+
+            for (i <- 0 until hdrH) {
+                // Left margin and '|'.
+                tbl.a(blank(margin.left)).a(HDR_VER)
+
+                for (j <- 0 until hdr.size) {
+                    val c = hdr(j)
+
+                    if (i >= 0 && i < c.height)
+                        tbl.a(aligned(c.lines(i), colWs(j), c.style))
+                    else
+                        tbl.a(blank(colWs(j)))
+
+                    tbl.a(HDR_VER) // '|'
+                }
+
+                // Right margin.
+                tbl.a(blank(margin.right)).a(NL)
+            }
+
+            tbl.a(blank(margin.left)).a(HDR_CRS).a(dash(HDR_HOR, 
tblW)).a(HDR_CRS).a(blank(margin.right)).a(NL)
+        }
+        else
+            tbl.a(blank(margin.left)).a(ROW_CRS).a(dash(ROW_HOR, 
tblW)).a(ROW_CRS).a(blank(margin.right)).a(NL)
+
+        // Print rows, if any.
+        if (rows.nonEmpty) {
+            val horLine = (i: Int) => {
+                // Left margin and '+'
+                tbl.a(blank(margin.left)).a(ROW_CRS)
+
+                for (k <- 0 until rows(i).size)
+                    tbl.a(dash(ROW_HOR, colWs(k))).a(ROW_CRS)
+
+                // Right margin.
+                tbl.a(blank(margin.right)).a(NL)
+            }
+
+            for (i <- 0 until rows.size) {
+                val r = rows(i)
+
+                val rowH = rowHs(i)
+
+                if (i > 0 && ((rowH > 1 && autoBorder) || insideBorder) && 
rowHs(i - 1) == 1)
+                    horLine(i)
+
+                for (j <- 0 until rowH) {
+                    // Left margin and '|'
+                    tbl.a(blank(margin.left)).a(ROW_VER)
+
+                    for (k <- 0 until r.size) {
+                        val c = r(k)
+                        val w = colWs(k)
+
+                        if (j < c.height)
+                            tbl.a(aligned(c.lines(j), w, c.style))
+                        else
+                            tbl.a(blank(w))
+
+                        tbl.a(ROW_VER) // '|'
+                    }
+
+                    // Right margin.
+                    tbl.a(blank(margin.right)).a(NL)
+                }
+
+                if (i < rows.size - 1 && ((rowH > 1 && autoBorder) || 
insideBorder))
+                    horLine(i)
+            }
+
+            tbl.a(blank(margin.left)).a(ROW_CRS).a(dash(ROW_HOR, 
tblW)).a(ROW_CRS).a(blank(margin.right)).a(NL)
+        }
+
+        // Bottom margin.
+        for (i <- 1 to margin.bottom)
+            tbl.a(" ").a(NL)
+
+        print(tbl.toString)
+    }
+
+    def nonEmpty = rows.nonEmpty
+}
+
+/**
+ * Static context.
+ */
+object VisorTextTable {
+    /** Table header horizontal line. */
+    private val HDR_HOR = "="
+
+    /** Table header vertical line. */
+    private val HDR_VER = "|"
+
+    /** Table header crossroad line. */
+    private val HDR_CRS = "+"
+
+    /** Table row horizontal line. */
+    private val ROW_HOR = '-'
+
+    /** Table row vertical line. */
+    private val ROW_VER = '|'
+
+    /** Table row crossroad line. */
+    private val ROW_CRS = "+"
+
+    /**
+      * Creates new Visor text table.
+      */
+    def apply() =
+        new VisorTextTable
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/config/VisorConfigurationCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/config/VisorConfigurationCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/config/VisorConfigurationCommand.scala
index 447667a..e6e3782 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/config/VisorConfigurationCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/config/VisorConfigurationCommand.scala
@@ -18,18 +18,19 @@
 package org.apache.ignite.visor.commands.config
 
 import org.apache.ignite._
-import org.apache.ignite.cluster.ClusterNode
+import org.apache.ignite.internal.util.scala.impl
 import org.apache.ignite.internal.util.{IgniteUtils => U}
-import org.apache.ignite.internal.visor.util.VisorTaskUtils._
 import org.apache.ignite.lang.IgniteBiTuple
-
-import java.lang.System._
-
 import org.apache.ignite.visor.VisorTag
 import org.apache.ignite.visor.commands.cache.VisorCacheCommand
-import org.apache.ignite.visor.commands.{VisorConsoleCommand, VisorTextTable}
+import org.apache.ignite.visor.commands.common.{VisorConsoleCommand, 
VisorTextTable}
 import org.apache.ignite.visor.visor._
 
+import java.lang.System._
+
+import 
org.apache.ignite.internal.visor.node.VisorNodeConfigurationCollectorTask
+import org.apache.ignite.internal.visor.util.VisorTaskUtils._
+
 import scala.collection.JavaConversions._
 import scala.language.implicitConversions
 import scala.util.control.Breaks._
@@ -38,16 +39,6 @@ import scala.util.control.Breaks._
  * ==Overview==
  * Visor 'config' command implementation.
  *
- * ==Importing==
- * When using this command from Scala code (not from REPL) you need to make 
sure to
- * properly import all necessary typed and implicit conversions:
- * <ex>
- * import org.apache.ignite.visor._
- * import commands.config.VisorConfigurationCommand._
- * </ex>
- * Note that `VisorConfigurationCommand` object contains necessary implicit 
conversions so that
- * this command would be available via `visor` keyword.
- *
  * ==Help==
  * {{{
  * +-------------------------------------+
@@ -79,18 +70,8 @@ import scala.util.control.Breaks._
  *         Starts command in interactive mode.
  * }}}
  */
-class VisorConfigurationCommand {
-    /**
-     * Prints error message and advise.
-     *
-     * @param errMsgs Error messages.
-     */
-    private def scold(errMsgs: Any*) {
-        assert(errMsgs != null)
-
-        warn(errMsgs: _*)
-        warn("Type 'help config' to see how to use this command.")
-    }
+class VisorConfigurationCommand extends VisorConsoleCommand {
+    @impl protected val name = "config"
 
     /**
       * ===Command===
@@ -101,13 +82,13 @@ class VisorConfigurationCommand {
       * Starts command in interactive mode.
      */
     def config() {
-        if (!isConnected)
-            adviseToConnect()
-        else
+        if (isConnected)
             askForNode("Select node from:") match {
                 case Some(id) => config("-id=" + id)
                 case None => ()
             }
+        else
+            adviseToConnect()
     }
 
     /**
@@ -130,63 +111,28 @@ class VisorConfigurationCommand {
 
             val argLst = parseArgs(args)
 
-            val id8 = argValue("id8", argLst)
-            val id = argValue("id", argLst)
-
-            var node: ClusterNode = null
-
-            if (id8.isEmpty && id.isEmpty) {
-                scold("One of -id8 or -id is required.")
-
-                break()
-            }
-
-            if (id8.isDefined && id.isDefined) {
-                scold("Only one of -id8 or -id is allowed.")
-
-                break()
-            }
-
-            if (id8.isDefined) {
-                val ns = nodeById8(id8.get)
-
-                if (ns.isEmpty) {
-                    scold("Unknown 'id8' value: " + id8.get)
-
-                    break()
-                }
-                else if (ns.size != 1) {
-                    scold("'id8' resolves to more than one node (use full 'id' 
instead): " + id8.get)
+            val nid = parseNode(argLst) match {
+                case Left(msg) =>
+                    scold(msg)
 
                     break()
-                }
-                else
-                    node = ns.head
-            }
-            else if (id.isDefined)
-                try {
-                    node = 
ignite.cluster.node(java.util.UUID.fromString(id.get))
 
-                    if (node == null) {
-                        scold("'id' does not match any node: " + id.get)
+                case Right(None) =>
+                    scold("One of -id8 or -id is required.")
 
-                        break()
-                    }
-                }
-                catch {
-                    case e: IllegalArgumentException =>
-                        scold("Invalid node 'id': " + id.get)
+                    break()
 
-                        break()
-                }
+                case Right(Some(n)) =>
+                    assert(n != null)
 
-            assert(node != null)
+                    n.id()
+            }
 
             val cfg = try
-                nodeConfiguration(node.id())
+                executeOne(nid, classOf[VisorNodeConfigurationCollectorTask], 
null)
             catch {
                 case e: IgniteException =>
-                    scold(e.getMessage)
+                    scold(e)
 
                     break()
             }
@@ -390,11 +336,11 @@ class VisorConfigurationCommand {
                 println("\nNo system properties defined.")
 
             try
-                cacheConfigurations(node.id).foreach(cacheCfg =>
+                cacheConfigurations(nid).foreach(cacheCfg =>
                     VisorCacheCommand.showCacheConfiguration("\nCache '" + 
escapeName(cacheCfg.name()) + "':", cacheCfg))
             catch {
                 case e: IgniteException =>
-                    scold(e.getMessage)
+                    scold(e)
             }
         }
     }
@@ -405,11 +351,10 @@ class VisorConfigurationCommand {
      * @param value String.
      * @return List of strings.
      */
-    def compactProperty(name: String, value: String): List[String] = {
+    private[this] def compactProperty(name: String, value: String): 
List[String] = {
         val ps = getProperty("path.separator")
 
-        // Split all values having path separator into multiple
-        // lines (with few exceptions...).
+        // Split all values having path separator into multiple lines (with 
few exceptions...).
         val lst =
             if (name != "path.separator" && value.indexOf(ps) != -1 && 
value.indexOf("http:") == -1 &&
                 value.length() > 80)
@@ -428,12 +373,15 @@ class VisorConfigurationCommand {
  * Companion object that does initialization of the command.
  */
 object VisorConfigurationCommand {
+    /** Singleton command. */
+    private val cmd = new VisorConfigurationCommand
+
     addHelp(
-        name = "config",
+        name = cmd.name,
         shortInfo = "Prints node configuration.",
         spec = List(
-            "config",
-            "config {-id=<node-id>|id8=<node-id8>}"
+            cmd.name,
+            s"${cmd.name} {-id=<node-id>|id8=<node-id8>}"
         ),
         args = List(
             "-id=<node-id>" -> List(
@@ -447,19 +395,17 @@ object VisorConfigurationCommand {
             )
         ),
         examples = List(
-            "config -id8=12345678" ->
+            s"${cmd.name} -id8=12345678" ->
                 "Prints configuration for node with '12345678' id8.",
-            "config -id8=@n0" ->
+            s"${cmd.name} -id8=@n0" ->
                 "Prints configuration for node with id8 taken from '@n0' 
memory variable.",
-            "config" ->
+            cmd.name ->
                 "Starts command in interactive mode."
         ),
-        ref = VisorConsoleCommand(cmd.config, cmd.config)
+        emptyArgs = cmd.config,
+        withArgs = cmd.config
     )
 
-    /** Singleton command. */
-    private val cmd = new VisorConfigurationCommand
-
     /**
      * Singleton.
      */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3a2c39f2/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/deploy/VisorDeployCommand.scala
----------------------------------------------------------------------
diff --git 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/deploy/VisorDeployCommand.scala
 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/deploy/VisorDeployCommand.scala
index 3856672..ac6a1dc 100644
--- 
a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/deploy/VisorDeployCommand.scala
+++ 
b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/deploy/VisorDeployCommand.scala
@@ -18,9 +18,13 @@
 package org.apache.ignite.visor.commands.deploy
 
 import org.apache.ignite.internal.util.io.GridFilenameUtils
+import org.apache.ignite.internal.util.lang.{GridFunc => F}
+import org.apache.ignite.internal.util.scala.impl
 import org.apache.ignite.internal.util.typedef.X
 import org.apache.ignite.internal.util.{IgniteUtils => U}
-import org.apache.ignite.internal.util.lang.{GridFunc => F}
+import org.apache.ignite.visor.VisorTag
+import org.apache.ignite.visor.commands.common.VisorConsoleCommand
+import org.apache.ignite.visor.visor._
 
 import com.jcraft.jsch._
 
@@ -28,10 +32,6 @@ import java.io._
 import java.net.UnknownHostException
 import java.util.concurrent._
 
-import org.apache.ignite.visor.VisorTag
-import org.apache.ignite.visor.commands.VisorConsoleCommand
-import org.apache.ignite.visor.visor._
-
 import scala.language.{implicitConversions, reflectiveCalls}
 import scala.util.control.Breaks._
 
@@ -308,7 +308,9 @@ private case class VisorCopier(
  *         Copies file or directory to remote host (private key 
authentication).
  * }}}
  */
-class VisorDeployCommand {
+class VisorDeployCommand extends VisorConsoleCommand {
+    @impl protected val name: String = "deploy"
+
     /** Default port. */
     private val DFLT_PORT = 22
 
@@ -316,20 +318,6 @@ class VisorDeployCommand {
     private val RANGE_SMB = "~"
 
     /**
-     * Prints error message and advise.
-     *
-     * @param errMsgs Error messages.
-     */
-    private def scold(errMsgs: Any*) {
-        assert(errMsgs != null)
-
-        nl()
-
-        warn(errMsgs: _*)
-        warn("Type 'help deploy' to see how to use this command.")
-    }
-
-    /**
      * Catch point for missing arguments case.
      */
     def deploy() {
@@ -367,7 +355,7 @@ class VisorDeployCommand {
             try
                 hosts ++= mkHosts(h, dfltUname, dfltPasswd, key.isDefined)
             catch {
-                case e: IllegalArgumentException => scold(e.getMessage).^^
+                case e: IllegalArgumentException => scold(e).^^
             }
         })
 
@@ -524,15 +512,18 @@ class VisorDeployCommand {
  * Companion object that does initialization of the command.
  */
 object VisorDeployCommand {
+    /** Singleton command. */
+    private val cmd = new VisorDeployCommand
+
     addHelp(
-        name = "deploy",
+        name = cmd.name,
         shortInfo = "Copies file or folder to remote host.",
         longInfo = List(
             "Copies file or folder to remote host.",
             "Command relies on SFTP protocol."
         ),
         spec = List(
-            "deploy -h={<username>{:<password>}@}<host>{:<port>} 
{-u=<username>}",
+            s"${cmd.name} -h={<username>{:<password>}@}<host>{:<port>} 
{-u=<username>}",
             "    {-p=<password>} {-k=<path>} -s=<path> {-d<path>}"
         ),
         args = List(
@@ -568,17 +559,15 @@ object VisorDeployCommand {
             )
         ),
         examples = List(
-            "deploy -h=uname:passwd@host -s=/local/path -d=/remote/path" ->
+            s"${cmd.name} -h=uname:passwd@host -s=/local/path -d=/remote/path" 
->
                 "Copies file or folder to remote host (password 
authentication).",
-            "deploy -h=uname@host -k=ssh-key.pem -s=/local/path 
-d=/remote/path" ->
+            s"${cmd.name} -h=uname@host -k=ssh-key.pem -s=/local/path 
-d=/remote/path" ->
                 "Copies file or folder to remote host (private key 
authentication)."
         ),
-        ref = VisorConsoleCommand(cmd.deploy, cmd.deploy)
+        emptyArgs = cmd.deploy,
+        withArgs = cmd.deploy
     )
 
-    /** Singleton command. */
-    private val cmd = new VisorDeployCommand
-
     /**
      * Singleton.
      */

Reply via email to