http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/frontend/package.json
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/package.json 
b/modules/web-console/frontend/package.json
index 6f8fe4f..a6299df 100644
--- a/modules/web-console/frontend/package.json
+++ b/modules/web-console/frontend/package.json
@@ -88,7 +88,7 @@
     "json-loader": "0.5.7",
     "jsondiffpatch": "^0.2.5",
     "jszip": "3.1.5",
-    "lodash": "4.17.5",
+    "lodash": "4.17.10",
     "mini-css-extract-plugin": "^0.4.0",
     "natural-compare-lite": "^1.4.0",
     "node-sass": "^4.8.3",
@@ -107,11 +107,11 @@
     "svg-sprite-loader": "3.0.7",
     "tf-metatags": "2.0.0",
     "uglifyjs-webpack-plugin": "1.2.4",
-    "webpack": "4.5.0",
+    "webpack": "4.12.0",
     "webpack-cli": "2.0.14",
-    "webpack-dev-server": "3.1.1",
-    "webpack-merge": "4.1.2",
-    "worker-loader": "^1.1.1"
+    "webpack-dev-server": "3.1.4",
+    "webpack-merge": "4.1.3",
+    "worker-loader": "^2.0.0"
   },
   "devDependencies": {
     "@types/angular": "^1.6.32",
@@ -124,8 +124,9 @@
     "@types/mocha": "^2.2.48",
     "@types/sinon": "^4.0.0",
     "@types/ui-grid": "0.0.38",
-    "@types/webpack": "^4.1.2",
+    "@types/webpack": "^4.4.1",
     "@types/webpack-merge": "^4.1.3",
+    "@types/socket.io-client": "1.4.32",
     "angular-mocks": "^1.6.9",
     "app-root-path": "2.0.1",
     "chai": "4.1.0",

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/frontend/public/images/icons/exit.svg
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/public/images/icons/exit.svg 
b/modules/web-console/frontend/public/images/icons/exit.svg
new file mode 100644
index 0000000..a355dcd
--- /dev/null
+++ b/modules/web-console/frontend/public/images/icons/exit.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg"; width="16" height="18" viewBox="0 0 16 
18">
+    <path fill="currentColor" fill-rule="nonzero" d="M15.723 
8.354l-4.17-4.17a.633.633 0 0 0-.893.894l3.134 3.133H5.632a.632.632 0 0 0 0 
1.264h8.162l-3.091 3.091a.632.632 0 0 0 .893.893l4.17-4.17a.605.605 0 0 0 
.041-.045c.007-.007.011-.015.017-.023l.02-.027c.006-.01.01-.019.017-.028l.015-.025.013-.029c.005-.009.01-.018.013-.027l.01-.028.011-.03.007-.029.008-.031.005-.034.004-.027a.64.64
 0 0 0-.004-.152l-.005-.033-.008-.032-.007-.029-.01-.03a.249.249 0 0 
0-.01-.028l-.014-.027-.013-.029-.015-.025-.017-.028c-.006-.01-.013-.018-.02-.027l-.017-.023a.61.61
 0 0 0-.065-.067.304.304 0 0 0-.019-.022zM1 
0h8.614v1.374h-8.24V16.48h8.24v1.373H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1z"/>
+</svg>

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/frontend/public/images/icons/index.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/public/images/icons/index.js 
b/modules/web-console/frontend/public/images/icons/index.js
index 62b3ab8..5a8d851 100644
--- a/modules/web-console/frontend/public/images/icons/index.js
+++ b/modules/web-console/frontend/public/images/icons/index.js
@@ -37,5 +37,8 @@ export collapse from './collapse.svg';
 export expand from './expand.svg';
 export home from './home.svg';
 export refresh from './refresh.svg';
-export {default as eyeOpened} from './eyeOpened.svg';
-export {default as eyeClosed} from './eyeClosed.svg';
+export eyeOpened from './eyeOpened.svg';
+export eyeClosed from './eyeClosed.svg';
+export lockOpened from './lockOpened.svg';
+export lockClosed from './lockClosed.svg';
+export exit from './exit.svg';

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/frontend/public/images/icons/lockClosed.svg
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/public/images/icons/lockClosed.svg 
b/modules/web-console/frontend/public/images/icons/lockClosed.svg
new file mode 100644
index 0000000..22f81e3
--- /dev/null
+++ b/modules/web-console/frontend/public/images/icons/lockClosed.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg"; width="12" height="16" viewBox="0 0 12 
16">
+    <path fill="currentColor" fill-rule="nonzero" d="M5.714 
10.808v1.483a.286.286 0 0 0 .572 0v-1.483a.858.858 0 1 0-.572 
0zm-4-4.522v-2a4.285 4.285 0 1 1 8.572 0v2A1.719 1.719 0 0 1 12 8.006v5.703c0 
.956-.77 1.72-1.72 1.72H1.72c-.951 0-1.72-.77-1.72-1.72V8.005c0-.954.767-1.717 
1.714-1.72zm1.715 0H8.57v-2A2.574 2.574 0 0 0 6 1.714a2.57 2.57 0 0 0-2.571 
2.572v2z"/>
+</svg>

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/frontend/public/images/icons/lockOpened.svg
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/public/images/icons/lockOpened.svg 
b/modules/web-console/frontend/public/images/icons/lockOpened.svg
new file mode 100644
index 0000000..bbc22c8
--- /dev/null
+++ b/modules/web-console/frontend/public/images/icons/lockOpened.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg"; width="12" height="17" viewBox="0 0 12 
17">
+    <path fill="currentColor" fill-rule="nonzero" d="M10.286 4.285v-.428.5c0 
.587-.384 1.072-.857 1.072-.477 0-.858-.48-.858-1.072v-.5.428A2.573 2.573 0 0 0 
6 1.715a2.57 2.57 0 0 0-2.571 2.57v3.001c0 .096.005.192.015.285H10.286A1.719 
1.719 0 0 1 12 9.291v5.704c0 .955-.77 1.72-1.72 1.72H1.72c-.951 
0-1.72-.77-1.72-1.72V9.29c0-.954.767-1.717 1.714-1.72V4.285a4.285 4.285 0 1 1 
8.572 0zm-4.572 9.292a.286.286 0 0 0 .572 0v-1.483a.857.857 0 1 0-.572 
0v1.483z"/>
+</svg>

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/README.txt
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/README.txt 
b/modules/web-console/web-agent/README.txt
index cdffe7f..86f12b8 100644
--- a/modules/web-console/web-agent/README.txt
+++ b/modules/web-console/web-agent/README.txt
@@ -20,6 +20,8 @@ Configuration file:
     tokens
     server-uri
     node-uri
+    node-login
+    node-password
     driver-folder
 
   Example configuration file:
@@ -47,6 +49,10 @@ Options:
   -n, --node-uri
      Comma-separated list of URIs for connect to Ignite REST server,
      default value: http://localhost:8080
+  -nl, --node-login
+     User name that will be used to connect to secured cluster.
+  -np, --node-password
+     Password that will be used to connect to secured cluster
   -s, --server-uri
      URI for connect to Ignite Web Console via web-socket protocol,
      default value: http://localhost:3000

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
index 3a3d950..bb2a8a2 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
@@ -25,6 +25,7 @@ import java.io.Reader;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
 import org.apache.ignite.internal.util.typedef.F;
@@ -57,10 +58,21 @@ public class AgentConfiguration {
     private String srvUri;
 
     /** */
-    @Parameter(names = {"-n", "--node-uri"}, description = "Comma-separated 
URIs for connect to Ignite node via REST" +
-        "                        " +
-        "      Default value: " + DFLT_NODE_URI)
-    private String nodeUri;
+    @Parameter(names = {"-n", "--node-uri"},
+        description = "Comma-separated URIs for connect to Ignite node via 
REST" +
+            "                        " +
+            "      Default value: " + DFLT_NODE_URI)
+    private List<String> nodeURIs;
+
+    /** */
+    @Parameter(names = {"-nl", "--node-login"},
+        description = "User name that will be used to connect to secured 
cluster")
+    private String nodeLogin;
+
+    /** */
+    @Parameter(names = {"-np", "--node-password"},
+        description = "Password that will be used to connect to secured 
cluster")
+    private String nodePwd;
 
     /** URI for connect to Ignite demo node REST server */
     private String demoNodeUri;
@@ -116,17 +128,45 @@ public class AgentConfiguration {
     }
 
     /**
-     * @return Node URI.
+     * @return Node URIs.
+     */
+    public List<String> nodeURIs() {
+        return nodeURIs;
+    }
+
+    /**
+     * @param nodeURIs Node URIs.
+     */
+    public void nodeURIs(List<String> nodeURIs) {
+        this.nodeURIs = nodeURIs;
+    }
+
+    /**
+     * @return User name for agent to authenticate on node.
+     */
+    public String nodeLogin() {
+        return nodeLogin;
+    }
+
+    /**
+     * @param nodeLogin User name for agent to authenticate on node.
+     */
+    public void nodeLogin(String nodeLogin) {
+        this.nodeLogin = nodeLogin;
+    }
+
+    /**
+     * @return Agent password to authenticate on node.
      */
-    public String nodeUri() {
-        return nodeUri;
+    public String nodePassword() {
+        return nodePwd;
     }
 
     /**
-     * @param nodeUri Node URI.
+     * @param nodePwd Agent password to authenticate on node.
      */
-    public void nodeUri(String nodeUri) {
-        this.nodeUri = nodeUri;
+    public void nodePassword(String nodePwd) {
+        this.nodePwd = nodePwd;
     }
 
     /**
@@ -208,7 +248,17 @@ public class AgentConfiguration {
         val = (String)props.remove("node-uri");
 
         if (val != null)
-            nodeUri(val);
+            nodeURIs(new ArrayList<>(Arrays.asList(val.split(","))));
+
+        val = (String)props.remove("node-login");
+
+        if (val != null)
+            nodeLogin(val);
+
+        val = (String)props.remove("node-password");
+
+        if (val != null)
+            nodePassword(val);
 
         val = (String)props.remove("driver-folder");
 
@@ -217,29 +267,35 @@ public class AgentConfiguration {
     }
 
     /**
-     * @param cmd Command.
+     * @param cfg Config to merge with.
      */
-    public void merge(AgentConfiguration cmd) {
+    public void merge(AgentConfiguration cfg) {
         if (tokens == null)
-            tokens(cmd.tokens());
+            tokens(cfg.tokens());
 
         if (srvUri == null)
-            serverUri(cmd.serverUri());
+            serverUri(cfg.serverUri());
 
         if (srvUri == null)
             serverUri(DFLT_SERVER_URI);
 
-        if (nodeUri == null)
-            nodeUri(cmd.nodeUri());
+        if (nodeURIs == null)
+            nodeURIs(cfg.nodeURIs());
+
+        if (nodeURIs == null)
+            nodeURIs(Collections.singletonList(DFLT_NODE_URI));
 
-        if (nodeUri == null)
-            nodeUri(DFLT_NODE_URI);
+        if (nodeLogin == null)
+            nodeLogin(cfg.nodeLogin());
+
+        if (nodePwd == null)
+            nodePassword(cfg.nodePassword());
 
         if (driversFolder == null)
-            driversFolder(cmd.driversFolder());
+            driversFolder(cfg.driversFolder());
 
         if (disableDemo == null)
-            disableDemo(cmd.disableDemo());
+            disableDemo(cfg.disableDemo());
     }
 
     /** {@inheritDoc} */
@@ -247,7 +303,7 @@ public class AgentConfiguration {
         StringBuilder sb = new StringBuilder();
 
         if (!F.isEmpty(tokens)) {
-            sb.append("User's security tokens        : ");
+            sb.append("User's security tokens          : ");
 
             boolean first = true;
 
@@ -269,9 +325,14 @@ public class AgentConfiguration {
             sb.append('\n');
         }
 
-        sb.append("URI to Ignite node REST server: ").append(nodeUri == null ? 
DFLT_NODE_URI : nodeUri).append('\n');
-        sb.append("URI to Ignite Console server  : ").append(srvUri == null ? 
DFLT_SERVER_URI : srvUri).append('\n');
-        sb.append("Path to agent property file   : 
").append(configPath()).append('\n');
+        sb.append("URI to Ignite node REST server  : ")
+            .append(nodeURIs == null ? DFLT_NODE_URI : String.join(", ", 
nodeURIs)).append('\n');
+
+        if (nodeLogin != null)
+            sb.append("Login to Ignite node REST server: 
").append(nodeLogin).append('\n');
+
+        sb.append("URI to Ignite Console server    : ").append(srvUri == null 
? DFLT_SERVER_URI : srvUri).append('\n');
+        sb.append("Path to agent property file     : 
").append(configPath()).append('\n');
 
         String drvFld = driversFolder();
 
@@ -282,8 +343,8 @@ public class AgentConfiguration {
                 drvFld = new File(agentHome, "jdbc-drivers").getPath();
         }
 
-        sb.append("Path to JDBC drivers folder   : 
").append(drvFld).append('\n');
-        sb.append("Demo mode                     : ").append(disableDemo() ? 
"disabled" : "enabled");
+        sb.append("Path to JDBC drivers folder     : 
").append(drvFld).append('\n');
+        sb.append("Demo mode                       : ").append(disableDemo() ? 
"disabled" : "enabled");
 
         return sb.toString();
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
index 385ce08..9340417 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
@@ -32,7 +32,7 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.UnknownHostException;
-import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -43,12 +43,10 @@ import java.util.jar.Manifest;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
 import org.apache.ignite.console.agent.handlers.ClusterListener;
-import org.apache.ignite.console.agent.handlers.DemoListener;
-import org.apache.ignite.console.agent.rest.RestExecutor;
 import org.apache.ignite.console.agent.handlers.DatabaseListener;
 import org.apache.ignite.console.agent.handlers.RestListener;
+import org.apache.ignite.console.agent.rest.RestExecutor;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.X;
 import org.json.JSONArray;
@@ -64,6 +62,7 @@ import static io.socket.client.Socket.EVENT_DISCONNECT;
 import static io.socket.client.Socket.EVENT_ERROR;
 import static org.apache.ignite.console.agent.AgentUtils.fromJSON;
 import static org.apache.ignite.console.agent.AgentUtils.toJSON;
+import static org.apache.ignite.console.agent.AgentUtils.trustManager;
 
 /**
  * Ignite Web Agent launcher.
@@ -73,21 +72,6 @@ public class AgentLauncher {
     private static final Logger log = 
LoggerFactory.getLogger(AgentLauncher.class);
 
     /** */
-    private static final String EVENT_CLUSTER_BROADCAST_START = 
"cluster:broadcast:start";
-
-    /** */
-    private static final String EVENT_CLUSTER_BROADCAST_STOP = 
"cluster:broadcast:stop";
-
-    /** */
-    private static final String EVENT_CLUSTER_DISCONNECTED = 
"cluster:disconnected";
-
-    /** */
-    private static final String EVENT_DEMO_BROADCAST_START = 
"demo:broadcast:start";
-
-    /** */
-    private static final String EVENT_DEMO_BROADCAST_STOP = 
"demo:broadcast:stop";
-
-    /** */
     private static final String EVENT_SCHEMA_IMPORT_DRIVERS = 
"schemaImport:drivers";
 
     /** */
@@ -103,7 +87,7 @@ public class AgentLauncher {
     private static final String EVENT_NODE_REST = "node:rest";
 
     /** */
-    private static final String EVENT_RESET_TOKENS = "agent:reset:token";
+    private static final String EVENT_RESET_TOKEN = "agent:reset:token";
 
     /** */
     private static final String EVENT_LOG_WARNING = "log:warn";
@@ -117,118 +101,78 @@ public class AgentLauncher {
     }
 
     /**
-     * Create a trust manager that trusts all certificates It is not using a 
particular keyStore
-     */
-    private static TrustManager[] getTrustManagers() {
-        return new TrustManager[] {
-            new X509TrustManager() {
-                /** {@inheritDoc} */
-                @Override public X509Certificate[] getAcceptedIssuers() {
-                    return new X509Certificate[0];
-                }
-
-                /** {@inheritDoc} */
-                @Override public void checkClientTrusted(X509Certificate[] 
certs, String authType) {
-                }
-
-                /** {@inheritDoc} */
-                @Override public void checkServerTrusted(X509Certificate[] 
certs, String authType) {
-                }
-            }};
-    }
-
-    /**
      * On error listener.
      */
-    private static final Emitter.Listener onError = new Emitter.Listener() {
-        @Override public void call(Object... args) {
-            Throwable e = (Throwable)args[0];
+    private static final Emitter.Listener onError = args -> {
+        Throwable e = (Throwable)args[0];
 
-            ConnectException ce = X.cause(e, ConnectException.class);
+        ConnectException ce = X.cause(e, ConnectException.class);
 
-            if (ce != null)
-                log.error("Failed to establish connection to server 
(connection refused).");
-            else {
-                Exception ignore = X.cause(e, SSLHandshakeException.class);
+        if (ce != null)
+            log.error("Failed to establish connection to server (connection 
refused).");
+        else {
+            Exception ignore = X.cause(e, SSLHandshakeException.class);
 
-                if (ignore != null) {
-                    log.error("Failed to establish SSL connection to server, 
due to errors with SSL handshake.");
-                    log.error("Add to environment variable JVM_OPTS parameter 
\"-Dtrust.all=true\" to skip certificate validation in case of using 
self-signed certificate.");
+            if (ignore != null) {
+                log.error("Failed to establish SSL connection to server, due 
to errors with SSL handshake.");
+                log.error("Add to environment variable JVM_OPTS parameter 
\"-Dtrust.all=true\" to skip certificate validation in case of using 
self-signed certificate.");
 
-                    System.exit(1);
-                }
+                System.exit(1);
+            }
 
-                ignore = X.cause(e, UnknownHostException.class);
+            ignore = X.cause(e, UnknownHostException.class);
 
-                if (ignore != null) {
-                    log.error("Failed to establish connection to server, due 
to errors with DNS or missing proxy settings.");
-                    log.error("Documentation for proxy configuration can be 
found here: 
http://apacheignite.readme.io/docs/web-agent#section-proxy-configuration";);
+            if (ignore != null) {
+                log.error("Failed to establish connection to server, due to 
errors with DNS or missing proxy settings.");
+                log.error("Documentation for proxy configuration can be found 
here: 
http://apacheignite.readme.io/docs/web-agent#section-proxy-configuration";);
 
-                    System.exit(1);
-                }
-
-                ignore = X.cause(e, IOException.class);
+                System.exit(1);
+            }
 
-                if (ignore != null && "404".equals(ignore.getMessage())) {
-                    log.error("Failed to receive response from server 
(connection refused).");
+            ignore = X.cause(e, IOException.class);
 
-                    return;
-                }
+            if (ignore != null && "404".equals(ignore.getMessage())) {
+                log.error("Failed to receive response from server (connection 
refused).");
 
-                if (ignore != null && "407".equals(ignore.getMessage())) {
-                    log.error("Failed to establish connection to server, due 
to proxy requires authentication.");
+                return;
+            }
 
-                    String userName = 
System.getProperty("https.proxyUsername", 
System.getProperty("http.proxyUsername"));
+            if (ignore != null && "407".equals(ignore.getMessage())) {
+                log.error("Failed to establish connection to server, due to 
proxy requires authentication.");
 
-                    if (userName == null || userName.trim().isEmpty())
-                        userName = readLine("Enter proxy user name: ");
-                    else
-                        System.out.println("Read username from system 
properties: " + userName);
+                String userName = System.getProperty("https.proxyUsername", 
System.getProperty("http.proxyUsername"));
 
-                    char[] pwd = readPassword("Enter proxy password: ");
+                if (userName == null || userName.trim().isEmpty())
+                    userName = readLine("Enter proxy user name: ");
+                else
+                    System.out.println("Read username from system properties: 
" + userName);
 
-                    final PasswordAuthentication pwdAuth = new 
PasswordAuthentication(userName, pwd);
+                char[] pwd = readPassword("Enter proxy password: ");
 
-                    Authenticator.setDefault(new Authenticator() {
-                        @Override protected PasswordAuthentication 
getPasswordAuthentication() {
-                            return pwdAuth;
-                        }
-                    });
+                final PasswordAuthentication pwdAuth = new 
PasswordAuthentication(userName, pwd);
 
-                    return;
-                }
+                Authenticator.setDefault(new Authenticator() {
+                    @Override protected PasswordAuthentication 
getPasswordAuthentication() {
+                        return pwdAuth;
+                    }
+                });
 
-                log.error("Connection error.", e);
+                return;
             }
+
+            log.error("Connection error.", e);
         }
     };
 
     /**
      * On disconnect listener.
      */
-    private static final Emitter.Listener onDisconnect = new 
Emitter.Listener() {
-        @Override public void call(Object... args) {
-            log.error("Connection closed: {}", args);
-        }
-    };
+    private static final Emitter.Listener onDisconnect = args -> 
log.error("Connection closed: {}", args);
 
     /**
      * On token reset listener.
      */
-    private static final Emitter.Listener onLogWarning = new 
Emitter.Listener() {
-        @Override public void call(Object... args) {
-            log.warn(String.valueOf(args[0]));
-        }
-    };
-
-    /**
-     * On demo start request.
-     */
-    private static final Emitter.Listener onDemoStart = new Emitter.Listener() 
{
-        @Override public void call(Object... args) {
-            log.warn(String.valueOf(args[0]));
-        }
-    };
+    private static final Emitter.Listener onLogWarning = args -> 
log.warn(String.valueOf(args[0]));
 
     /**
      * @param fmt Format string.
@@ -311,28 +255,26 @@ public class AgentLauncher {
         System.out.println(cfg);
         System.out.println();
 
-        if (cfg.tokens() == null) {
-            String webHost;
+        URI uri;
 
-            try {
-                webHost = new URI(cfg.serverUri()).getHost();
-            }
-            catch (URISyntaxException e) {
-                log.error("Failed to parse Ignite Web Console uri", e);
+        try {
+            uri = new URI(cfg.serverUri());
+        }
+        catch (URISyntaxException e) {
+            log.error("Failed to parse Ignite Web Console uri", e);
 
-                return;
-            }
+            return;
+        }
 
+        if (cfg.tokens() == null) {
             System.out.println("Security token is required to establish 
connection to the web console.");
-            System.out.println(String.format("It is available on the Profile 
page: https://%s/profile";, webHost));
+            System.out.println(String.format("It is available on the Profile 
page: https://%s/profile";, uri.getHost()));
 
             String tokens = String.valueOf(readPassword("Enter security tokens 
separated by comma: "));
 
-            cfg.tokens(Arrays.asList(tokens.trim().split(",")));
+            cfg.tokens(new 
ArrayList<>(Arrays.asList(tokens.trim().split(","))));
         }
 
-        URI uri = URI.create(cfg.serverUri());
-
         // Create proxy authenticator using passed properties.
         switch (uri.getScheme()) {
             case "http":
@@ -352,6 +294,29 @@ public class AgentLauncher {
                 // No-op.
         }
 
+        List<String> nodeURIs = cfg.nodeURIs();
+
+        for (int i = nodeURIs.size() - 1; i >= 0; i--) {
+            String nodeURI = nodeURIs.get(i);
+
+            try {
+                new URI(nodeURI);
+            }
+            catch (URISyntaxException ignored) {
+                log.warn("Failed to parse Ignite node URI: {}.", nodeURI);
+
+                nodeURIs.remove(i);
+            }
+        }
+
+        if (nodeURIs.isEmpty()) {
+            log.error("Failed to find valid URIs for connect to Ignite node 
via REST. Please check agent settings");
+
+            return;
+        }
+
+        cfg.nodeURIs(nodeURIs);
+
         IO.Options opts = new IO.Options();
 
         opts.path = "/agents";
@@ -361,102 +326,95 @@ public class AgentLauncher {
             SSLContext ctx = SSLContext.getInstance("TLS");
 
             // Create an SSLContext that uses our TrustManager
-            ctx.init(null, getTrustManagers(), null);
+            ctx.init(null, new TrustManager[] {trustManager()}, null);
 
             opts.sslContext = ctx;
         }
 
         final Socket client = IO.socket(uri, opts);
-        final RestExecutor restExecutor = new RestExecutor(cfg.nodeUri());
 
-        try {
-            final ClusterListener clusterLsnr = new ClusterListener(client, 
restExecutor);
-            final DemoListener demoHnd = new DemoListener(client, 
restExecutor);
-
-            Emitter.Listener onConnect = new Emitter.Listener() {
-                @Override public void call(Object... args) {
-                    log.info("Connection established.");
-
-                    JSONObject authMsg = new JSONObject();
+        try (RestExecutor restExecutor = new RestExecutor();
+             ClusterListener clusterLsnr = new ClusterListener(cfg, client, 
restExecutor)) {
+            Emitter.Listener onConnect = connectRes -> {
+                log.info("Connection established.");
 
-                    try {
-                        authMsg.put("tokens", toJSON(cfg.tokens()));
-                        authMsg.put("disableDemo", cfg.disableDemo());
+                JSONObject authMsg = new JSONObject();
 
-                        String clsName = AgentLauncher.class.getSimpleName() + 
".class";
+                try {
+                    authMsg.put("tokens", toJSON(cfg.tokens()));
+                    authMsg.put("disableDemo", cfg.disableDemo());
 
-                        String clsPath = 
AgentLauncher.class.getResource(clsName).toString();
+                    String clsName = AgentLauncher.class.getSimpleName() + 
".class";
 
-                        if (clsPath.startsWith("jar")) {
-                            String manifestPath = clsPath.substring(0, 
clsPath.lastIndexOf('!') + 1) +
-                                "/META-INF/MANIFEST.MF";
+                    String clsPath = 
AgentLauncher.class.getResource(clsName).toString();
 
-                            Manifest manifest = new Manifest(new 
URL(manifestPath).openStream());
+                    if (clsPath.startsWith("jar")) {
+                        String manifestPath = clsPath.substring(0, 
clsPath.lastIndexOf('!') + 1) +
+                            "/META-INF/MANIFEST.MF";
 
-                            Attributes attr = manifest.getMainAttributes();
+                        Manifest manifest = new Manifest(new 
URL(manifestPath).openStream());
 
-                            authMsg.put("ver", 
attr.getValue("Implementation-Version"));
-                            authMsg.put("bt", attr.getValue("Build-Time"));
-                        }
+                        Attributes attr = manifest.getMainAttributes();
 
-                        client.emit("agent:auth", authMsg, new Ack() {
-                            @Override public void call(Object... args) {
-                                if (args != null) {
-                                    if (args[0] instanceof String) {
-                                        log.error((String)args[0]);
+                        authMsg.put("ver", 
attr.getValue("Implementation-Version"));
+                        authMsg.put("bt", attr.getValue("Build-Time"));
+                    }
 
-                                        System.exit(1);
-                                    }
+                    client.emit("agent:auth", authMsg, (Ack) authRes -> {
+                        if (authRes != null) {
+                            if (authRes[0] instanceof String) {
+                                log.error((String)authRes[0]);
 
-                                    if (args[0] == null && args[1] instanceof 
JSONArray) {
-                                        try {
-                                            List<String> activeTokens = 
fromJSON(args[1], List.class);
+                                System.exit(1);
+                            }
 
-                                            if (!F.isEmpty(activeTokens)) {
-                                                Collection<String> 
missedTokens = cfg.tokens();
+                            if (authRes[0] == null && authRes[1] instanceof 
JSONArray) {
+                                try {
+                                    List<String> activeTokens = 
fromJSON(authRes[1], List.class);
 
-                                                cfg.tokens(activeTokens);
+                                    if (!F.isEmpty(activeTokens)) {
+                                        Collection<String> missedTokens = 
cfg.tokens();
 
-                                                
missedTokens.removeAll(activeTokens);
+                                        cfg.tokens(activeTokens);
 
-                                                if (!F.isEmpty(missedTokens)) {
-                                                    String tokens = 
F.concat(missedTokens, ", ");
+                                        missedTokens.removeAll(activeTokens);
 
-                                                    log.warn("Failed to 
authenticate with token(s): {}. " +
-                                                        "Please reload agent 
archive or check settings", tokens);
-                                                }
+                                        if (!F.isEmpty(missedTokens)) {
+                                            String tokens = 
F.concat(missedTokens, ", ");
 
-                                                log.info("Authentication 
success.");
+                                            log.warn("Failed to authenticate 
with token(s): {}. " +
+                                                "Please reload agent archive 
or check settings", tokens);
+                                        }
 
-                                                clusterLsnr.watch();
+                                        log.info("Authentication success.");
 
-                                                return;
-                                            }
-                                        }
-                                        catch (Exception e) {
-                                            log.error("Failed to authenticate 
agent. Please check agent\'s tokens", e);
+                                        clusterLsnr.watch();
 
-                                            System.exit(1);
-                                        }
+                                        return;
                                     }
                                 }
+                                catch (Exception e) {
+                                    log.error("Failed to authenticate agent. 
Please check agent\'s tokens", e);
 
-                                log.error("Failed to authenticate agent. 
Please check agent\'s tokens");
-
-                                System.exit(1);
+                                    System.exit(1);
+                                }
                             }
-                        });
-                    }
-                    catch (JSONException | IOException e) {
-                        log.error("Failed to construct authentication 
message", e);
+                        }
 
-                        client.close();
-                    }
+                        log.error("Failed to authenticate agent. Please check 
agent\'s tokens");
+
+                        System.exit(1);
+                    });
+                }
+                catch (JSONException | IOException e) {
+                    log.error("Failed to construct authentication message", e);
+
+                    client.close();
                 }
             };
 
             DatabaseListener dbHnd = new DatabaseListener(cfg);
-            RestListener restHnd = new RestListener(restExecutor);
+            RestListener restHnd = new RestListener(cfg, restExecutor);
 
             final CountDownLatch latch = new CountDownLatch(1);
 
@@ -468,23 +426,17 @@ public class AgentLauncher {
                 .on(EVENT_ERROR, onError)
                 .on(EVENT_DISCONNECT, onDisconnect)
                 .on(EVENT_LOG_WARNING, onLogWarning)
-                .on(EVENT_CLUSTER_BROADCAST_START, clusterLsnr.start())
-                .on(EVENT_CLUSTER_BROADCAST_STOP, clusterLsnr.stop())
-                .on(EVENT_DEMO_BROADCAST_START, demoHnd.start())
-                .on(EVENT_DEMO_BROADCAST_STOP, demoHnd.stop())
-                .on(EVENT_RESET_TOKENS, new Emitter.Listener() {
-                    @Override public void call(Object... args) {
-                        String tok = String.valueOf(args[0]);
+                .on(EVENT_RESET_TOKEN, res -> {
+                    String tok = String.valueOf(res[0]);
 
-                        log.warn("Security token has been reset: {}", tok);
+                    log.warn("Security token has been reset: {}", tok);
 
-                        cfg.tokens().remove(tok);
+                    cfg.tokens().remove(tok);
 
-                        if (cfg.tokens().isEmpty()) {
-                            client.off();
+                    if (cfg.tokens().isEmpty()) {
+                        client.off();
 
-                            latch.countDown();
-                        }
+                        latch.countDown();
                     }
                 })
                 .on(EVENT_SCHEMA_IMPORT_DRIVERS, 
dbHnd.availableDriversListener())
@@ -498,8 +450,6 @@ public class AgentLauncher {
             latch.await();
         }
         finally {
-            restExecutor.stop();
-
             client.close();
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
index 797951c..38edd97 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
@@ -24,7 +24,9 @@ import java.io.File;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.ProtectionDomain;
+import java.security.cert.X509Certificate;
 import java.util.Arrays;
+import javax.net.ssl.X509TrustManager;
 import org.apache.log4j.Logger;
 import org.json.JSONArray;
 import org.json.JSONObject;
@@ -182,4 +184,26 @@ public class AgentUtils {
     public static <T> T fromJSON(Object obj, Class<T> toValType) throws 
IllegalArgumentException {
         return MAPPER.convertValue(obj, toValType);
     }
+
+    /**
+     * Create a trust manager that trusts all certificates It is not using a 
particular keyStore
+     */
+    public static X509TrustManager trustManager() {
+        return new X509TrustManager() {
+            /** {@inheritDoc} */
+            @Override public X509Certificate[] getAcceptedIssuers() {
+                return new X509Certificate[0];
+            }
+
+            /** {@inheritDoc} */
+            @Override public void checkClientTrusted(X509Certificate[] certs, 
String authType) {
+                // No-op.
+            }
+
+            /** {@inheritDoc} */
+            @Override public void checkServerTrusted(X509Certificate[] certs, 
String authType) {
+                // No-op.
+            }
+        };
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java
index ace2087..33e4c2b 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java
@@ -28,8 +28,10 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.zip.GZIPOutputStream;
 import org.apache.commons.codec.binary.Base64OutputStream;
+import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.console.agent.rest.RestResult;
-import org.apache.log4j.Logger;
+import org.apache.ignite.logger.slf4j.Slf4jLogger;
+import org.slf4j.LoggerFactory;
 
 import static org.apache.ignite.console.agent.AgentUtils.removeCallback;
 import static org.apache.ignite.console.agent.AgentUtils.fromJSON;
@@ -40,15 +42,15 @@ import static 
org.apache.ignite.console.agent.AgentUtils.toJSON;
  * Base class for web socket handlers.
  */
 abstract class AbstractListener implements Emitter.Listener {
+    /** */
+    final IgniteLogger log = new 
Slf4jLogger(LoggerFactory.getLogger(AbstractListener.class));
+
     /** UTF8 charset. */
     private static final Charset UTF8 = Charset.forName("UTF-8");
 
     /** */
     private ExecutorService pool;
 
-    /** */
-    final Logger log = Logger.getLogger(this.getClass().getName());
-
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
     @Override public final void call(Object... args) {
@@ -69,34 +71,32 @@ abstract class AbstractListener implements Emitter.Listener 
{
             if (pool == null)
                 pool = newThreadPool();
 
-            pool.submit(new Runnable() {
-                @Override public void run() {
-                    try {
-                        Object res = execute(params);
+            pool.submit(() -> {
+                try {
+                    Object res = execute(params);
 
-                        // TODO IGNITE-6127 Temporary solution until GZip 
support for socket.io-client-java.
-                        // See: 
https://github.com/socketio/socket.io-client-java/issues/312
-                        // We can GZip manually for now.
-                        if (res instanceof RestResult) {
-                            RestResult restRes = (RestResult) res;
+                    // TODO IGNITE-6127 Temporary solution until GZip support 
for socket.io-client-java.
+                    // See: 
https://github.com/socketio/socket.io-client-java/issues/312
+                    // We can GZip manually for now.
+                    if (res instanceof RestResult) {
+                        RestResult restRes = (RestResult) res;
 
-                            if (restRes.getData() != null) {
-                                ByteArrayOutputStream baos = new 
ByteArrayOutputStream(4096);
-                                Base64OutputStream b64os = new 
Base64OutputStream(baos, true, 0, null);
-                                GZIPOutputStream gzip = new 
GZIPOutputStream(b64os);
+                        if (restRes.getData() != null) {
+                            ByteArrayOutputStream baos = new 
ByteArrayOutputStream(4096);
+                            Base64OutputStream b64os = new 
Base64OutputStream(baos, true, 0, null);
+                            GZIPOutputStream gzip = new 
GZIPOutputStream(b64os);
 
-                                gzip.write(restRes.getData().getBytes(UTF8));
+                            gzip.write(restRes.getData().getBytes(UTF8));
 
-                                gzip.close();
+                            gzip.close();
 
-                                restRes.zipData(baos.toString());
-                            }
+                            restRes.zipData(baos.toString());
                         }
-
-                        cb.call(null, toJSON(res));
-                    } catch (Exception e) {
-                        cb.call(e, null);
                     }
+
+                    cb.call(null, toJSON(res));
+                } catch (Exception e) {
+                    cb.call(e, null);
                 }
             });
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
index 86b9ea5..0c7560c 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
@@ -17,10 +17,8 @@
 
 package org.apache.ignite.console.agent.handlers;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import io.socket.client.Socket;
-import io.socket.emitter.Emitter;
+import java.io.IOException;
 import java.net.ConnectException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -33,6 +31,11 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import org.apache.ignite.IgniteLogger;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.ignite.console.agent.AgentConfiguration;
 import org.apache.ignite.console.agent.rest.RestExecutor;
 import org.apache.ignite.console.agent.rest.RestResult;
 import 
org.apache.ignite.internal.processors.rest.client.message.GridClientNodeBean;
@@ -51,17 +54,30 @@ import static 
org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
 import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_CLIENT_MODE;
 import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_IPS;
 import static 
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_SUCCESS;
+import static 
org.apache.ignite.internal.processors.rest.client.message.GridClientResponse.STATUS_FAILED;
 import static 
org.apache.ignite.internal.visor.util.VisorTaskUtils.sortAddresses;
 import static 
org.apache.ignite.internal.visor.util.VisorTaskUtils.splitAddresses;
 
 /**
  * API to transfer topology from Ignite cluster available by node-uri.
  */
-public class ClusterListener {
+public class ClusterListener implements AutoCloseable {
     /** */
     private static final IgniteLogger log = new 
Slf4jLogger(LoggerFactory.getLogger(ClusterListener.class));
 
     /** */
+    private static final IgniteProductVersion IGNITE_2_1 = 
IgniteProductVersion.fromString("2.1.0");
+
+    /** */
+    private static final IgniteProductVersion IGNITE_2_3 = 
IgniteProductVersion.fromString("2.3.0");
+
+    /** Unique Visor key to get events last order. */
+    private static final String EVT_LAST_ORDER_KEY = "WEB_AGENT_" + 
UUID.randomUUID().toString();
+
+    /** Unique Visor key to get events throttle counter. */
+    private static final String EVT_THROTTLE_CNTR_KEY = "WEB_AGENT_" + 
UUID.randomUUID().toString();
+
+    /** */
     private static final String EVENT_CLUSTER_CONNECTED = "cluster:connected";
 
     /** */
@@ -83,9 +99,6 @@ public class ClusterListener {
     private final WatchTask watchTask = new WatchTask();
 
     /** */
-    private final BroadcastTask broadcastTask = new BroadcastTask();
-
-    /** */
     private static final IgniteClosure<UUID, String> ID2ID8 = new 
IgniteClosure<UUID, String>() {
         @Override public String apply(UUID nid) {
             return U.id8(nid).toUpperCase();
@@ -97,10 +110,7 @@ public class ClusterListener {
     };
 
     /** */
-    private static final ScheduledExecutorService pool = 
Executors.newScheduledThreadPool(1);
-
-    /** */
-    private ScheduledFuture<?> refreshTask;
+    private AgentConfiguration cfg;
 
     /** */
     private Socket client;
@@ -108,11 +118,18 @@ public class ClusterListener {
     /** */
     private RestExecutor restExecutor;
 
+    /** */
+    private static final ScheduledExecutorService pool = 
Executors.newScheduledThreadPool(1);
+
+    /** */
+    private ScheduledFuture<?> refreshTask;
+
     /**
      * @param client Client.
      * @param restExecutor Client.
      */
-    public ClusterListener(Socket client, RestExecutor restExecutor) {
+    public ClusterListener(AgentConfiguration cfg, Socket client, RestExecutor 
restExecutor) {
+        this.cfg = cfg;
         this.client = client;
         this.restExecutor = restExecutor;
     }
@@ -159,32 +176,11 @@ public class ClusterListener {
         refreshTask = pool.scheduleWithFixedDelay(watchTask, 0L, DFLT_TIMEOUT, 
TimeUnit.MILLISECONDS);
     }
 
-    /**
-     * Start broadcast topology to server-side.
-     */
-    public Emitter.Listener start() {
-        return new Emitter.Listener() {
-            @Override public void call(Object... args) {
-                safeStopRefresh();
-
-                final long timeout = args.length > 1 && args[1] instanceof 
Long ? (long)args[1] : DFLT_TIMEOUT;
-
-                refreshTask = pool.scheduleWithFixedDelay(broadcastTask, 0L, 
timeout, TimeUnit.MILLISECONDS);
-            }
-        };
-    }
-
-    /**
-     * Stop broadcast topology to server-side.
-     */
-    public Emitter.Listener stop() {
-        return new Emitter.Listener() {
-            @Override public void call(Object... args) {
-                refreshTask.cancel(true);
+    /** {@inheritDoc} */
+    @Override public void close() {
+        refreshTask.cancel(true);
 
-                watch();
-            }
-        };
+        pool.shutdownNow();
     }
 
     /** */
@@ -210,6 +206,9 @@ public class ClusterListener {
         /** */
         private boolean active;
 
+        /** */
+        private boolean secured;
+
         /**
          * Helper method to get attribute.
          *
@@ -231,6 +230,7 @@ public class ClusterListener {
             addrs = U.newHashMap(sz);
             clients = U.newHashMap(sz);
             active = false;
+            secured = false;
 
             for (GridClientNodeBean node : nodes) {
                 UUID nid = node.getNodeId();
@@ -247,7 +247,7 @@ public class ClusterListener {
                 clients.put(nid, client);
 
                 Collection<String> nodeAddrs = client
-                    ? splitAddresses((String)attribute(attrs, ATTR_IPS))
+                    ? splitAddresses(attribute(attrs, ATTR_IPS))
                     : node.getTcpAddresses();
 
                 String firstIP = F.first(sortAddresses(nodeAddrs));
@@ -294,6 +294,20 @@ public class ClusterListener {
         }
 
         /**
+         * @return {@code true} If cluster has configured security.
+         */
+        public boolean isSecured() {
+            return secured;
+        }
+
+        /**
+         * @param secured Configured security flag.
+         */
+        public void setSecured(boolean secured) {
+            this.secured = secured;
+        }
+
+        /**
          * @return Cluster nodes IDs.
          */
         public Collection<UUID> getNids() {
@@ -339,54 +353,114 @@ public class ClusterListener {
 
     /** */
     private class WatchTask implements Runnable {
-        /** {@inheritDoc} */
-        @Override public void run() {
-            try {
-                RestResult res = restExecutor.topology(false, false);
+        /** */
+        private static final String EXPIRED_SES_ERROR_MSG = "Failed to handle 
request - unknown session token (maybe expired session)";
 
-                switch (res.getStatus()) {
-                    case STATUS_SUCCESS:
-                        List<GridClientNodeBean> nodes = 
MAPPER.readValue(res.getData(),
-                            new TypeReference<List<GridClientNodeBean>>() {});
+        /** */
+        private String sesTok;
 
-                        TopologySnapshot newTop = new TopologySnapshot(nodes);
+        /**
+         * Execute REST command under agent user.
+         *
+         * @param params Command params.
+         * @return Command result.
+         * @throws IOException If failed to execute.
+         */
+        private RestResult restCommand(Map<String, Object> params) throws 
IOException {
+            if (!F.isEmpty(sesTok))
+                params.put("sessionToken", sesTok);
+            else if (!F.isEmpty(cfg.nodeLogin()) && 
!F.isEmpty(cfg.nodePassword())) {
+                params.put("user", cfg.nodeLogin());
+                params.put("password", cfg.nodePassword());
+            }
 
-                        if (newTop.differentCluster(top))
-                            log.info("Connection successfully established to 
cluster with nodes: " + newTop.nid8());
+            RestResult res = restExecutor.sendRequest(cfg.nodeURIs(), params, 
null);
 
-                        boolean active = 
restExecutor.active(newTop.clusterVersion(), F.first(newTop.getNids()));
+            switch (res.getStatus()) {
+                case STATUS_SUCCESS:
+                    sesTok = res.getSessionToken();
 
-                        newTop.setActive(active);
+                    return res;
+                    
+                case STATUS_FAILED:
+                    if (res.getError().startsWith(EXPIRED_SES_ERROR_MSG)) {
+                        sesTok = null;
+                        
+                        params.remove("sessionToken");
 
-                        top = newTop;
+                        return restCommand(params);
+                    }
 
-                        client.emit(EVENT_CLUSTER_TOPOLOGY, toJSON(top));
+                default:
+                    return res;
+            }
+        }
 
-                        break;
+        /**
+         * Collect topology.
+         *
+         * @param full Full.
+         */
+        private RestResult topology(boolean full) throws IOException {
+            Map<String, Object> params = U.newHashMap(3);
 
-                    default:
-                        LT.warn(log, res.getError());
+            params.put("cmd", "top");
+            params.put("attr", true);
+            params.put("mtr", full);
 
-                        clusterDisconnect();
+            return restCommand(params);
+        }
+
+        /**
+         * @param ver Cluster version.
+         * @param nid Node ID.
+         * @return Cluster active state.
+         * @throws IOException If failed to collect cluster active state.
+         */
+        public boolean active(IgniteProductVersion ver, UUID nid) throws 
IOException {
+            Map<String, Object> params = U.newHashMap(10);
+
+            boolean v23 = ver.compareTo(IGNITE_2_3) >= 0;
+
+            if (v23)
+                params.put("cmd", "currentState");
+            else {
+                params.put("cmd", "exe");
+                params.put("name", 
"org.apache.ignite.internal.visor.compute.VisorGatewayTask");
+                params.put("p1", nid);
+                params.put("p2", 
"org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTask");
+                params.put("p3", 
"org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTaskArg");
+                params.put("p4", false);
+                params.put("p5", EVT_LAST_ORDER_KEY);
+                params.put("p6", EVT_THROTTLE_CNTR_KEY);
+
+                if (ver.compareTo(IGNITE_2_1) >= 0)
+                    params.put("p7", false);
+                else {
+                    params.put("p7", 10);
+                    params.put("p8", false);
                 }
             }
-            catch (ConnectException ignored) {
-                clusterDisconnect();
-            }
-            catch (Exception e) {
-                log.error("WatchTask failed", e);
 
-                clusterDisconnect();
+            RestResult res = restCommand(params);
+
+            switch (res.getStatus()) {
+                case STATUS_SUCCESS:
+                    if (v23)
+                        return Boolean.valueOf(res.getData());
+
+                    return res.getData().contains("\"active\":true");
+
+                default:
+                    throw new IOException(res.getError());
             }
         }
-    }
 
-    /** */
-    private class BroadcastTask implements Runnable {
+
         /** {@inheritDoc} */
         @Override public void run() {
             try {
-                RestResult res = restExecutor.topology(false, true);
+                RestResult res = topology(false);
 
                 switch (res.getStatus()) {
                     case STATUS_SUCCESS:
@@ -395,17 +469,17 @@ public class ClusterListener {
 
                         TopologySnapshot newTop = new TopologySnapshot(nodes);
 
-                        if (top.differentCluster(newTop)) {
-                            clusterDisconnect();
-
+                        if (newTop.differentCluster(top))
                             log.info("Connection successfully established to 
cluster with nodes: " + newTop.nid8());
 
-                            watch();
-                        }
+                        boolean active = active(newTop.clusterVersion(), 
F.first(newTop.getNids()));
+
+                        newTop.setActive(active);
+                        newTop.setSecured(!F.isEmpty(res.getSessionToken()));
 
                         top = newTop;
 
-                        client.emit(EVENT_CLUSTER_TOPOLOGY, res.getData());
+                        client.emit(EVENT_CLUSTER_TOPOLOGY, toJSON(top));
 
                         break;
 
@@ -415,12 +489,13 @@ public class ClusterListener {
                         clusterDisconnect();
                 }
             }
+            catch (ConnectException ignored) {
+                clusterDisconnect();
+            }
             catch (Exception e) {
-                log.error("BroadcastTask failed", e);
+                log.error("WatchTask failed", e);
 
                 clusterDisconnect();
-
-                watch();
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java
index 9da118d..b6bd623 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java
@@ -125,7 +125,7 @@ public class DatabaseListener {
 
     /** */
     private final AbstractListener availableDriversLsnr = new 
AbstractListener() {
-        @Override public Object execute(Map<String, Object> args) throws 
Exception {
+        @Override public Object execute(Map<String, Object> args) {
             if (driversFolder == null) {
                 log.info("JDBC drivers folder not specified, returning empty 
list");
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DemoListener.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DemoListener.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DemoListener.java
deleted file mode 100644
index ce42032..0000000
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DemoListener.java
+++ /dev/null
@@ -1,131 +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.console.agent.handlers;
-
-import io.socket.client.Ack;
-import io.socket.client.Socket;
-import io.socket.emitter.Emitter;
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import org.apache.ignite.console.agent.rest.RestExecutor;
-import org.apache.ignite.console.agent.rest.RestResult;
-import org.apache.ignite.console.demo.AgentClusterDemo;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.ignite.console.agent.AgentUtils.safeCallback;
-import static org.apache.ignite.console.agent.AgentUtils.toJSON;
-
-/**
- * API to retranslate topology from Ignite demo cluster.
- */
-public class DemoListener {
-    /** */
-    private static final String EVENT_DEMO_TOPOLOGY = "demo:topology";
-
-    /** Default timeout. */
-    private static final long DFLT_TIMEOUT = 3000L;
-
-    /** */
-    private static final Logger log = 
LoggerFactory.getLogger(DemoListener.class);
-
-    /** */
-    private static final ScheduledExecutorService pool = 
Executors.newScheduledThreadPool(2);
-
-    /** */
-    private ScheduledFuture<?> refreshTask;
-
-    /** */
-    private Socket client;
-
-    /** */
-    private RestExecutor restExecutor;
-
-    /**
-     * @param client Client.
-     * @param restExecutor Client.
-     */
-    public DemoListener(Socket client, RestExecutor restExecutor) {
-        this.client = client;
-        this.restExecutor = restExecutor;
-    }
-
-    /**
-     * Start broadcast topology to server-side.
-     */
-    public Emitter.Listener start() {
-        return new Emitter.Listener() {
-            @Override public void call(final Object... args) {
-                final Ack demoStartCb = safeCallback(args);
-
-                final long timeout = args.length > 1  && args[1] instanceof 
Long ? (long)args[1] : DFLT_TIMEOUT;
-
-                if (refreshTask != null)
-                    refreshTask.cancel(true);
-
-                final CountDownLatch latch = AgentClusterDemo.tryStart();
-
-                pool.schedule(new Runnable() {
-                    @Override public void run() {
-                        try {
-                            U.await(latch);
-
-                            demoStartCb.call();
-
-                            refreshTask = pool.scheduleWithFixedDelay(new 
Runnable() {
-                                @Override public void run() {
-                                    try {
-                                        RestResult top = 
restExecutor.topology(true, true);
-
-                                        client.emit(EVENT_DEMO_TOPOLOGY, 
toJSON(top));
-                                    }
-                                    catch (IOException e) {
-                                        log.info("Lost connection to the demo 
cluster", e);
-
-                                        stop().call(); // TODO WTF????
-                                    }
-                                }
-                            }, 0L, timeout, TimeUnit.MILLISECONDS);
-                        }
-                        catch (Exception e) {
-                            demoStartCb.call(e);
-                        }
-                    }
-                }, 0, TimeUnit.MILLISECONDS);
-            }
-        };
-    }
-
-    /**
-     * Stop broadcast topology to server-side.
-     */
-    public Emitter.Listener stop() {
-        return new Emitter.Listener() {
-            @Override public void call(Object... args) {
-                refreshTask.cancel(true);
-
-                AgentClusterDemo.stop();
-            }
-        };
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestListener.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestListener.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestListener.java
index 8855060..24c2097 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestListener.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestListener.java
@@ -20,19 +20,28 @@ package org.apache.ignite.console.agent.handlers;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import org.apache.ignite.console.agent.AgentConfiguration;
 import org.apache.ignite.console.agent.rest.RestExecutor;
+import org.apache.ignite.console.agent.rest.RestResult;
+import org.apache.ignite.console.demo.AgentClusterDemo;
+import org.apache.ignite.internal.util.typedef.internal.U;
 
 /**
  * API to translate REST requests to Ignite cluster.
  */
 public class RestListener extends AbstractListener {
     /** */
+    private final AgentConfiguration cfg;
+
+    /** */
     private final RestExecutor restExecutor;
 
     /**
-     * @param restExecutor Config.
+     * @param cfg Config.
+     * @param restExecutor Executor.
      */
-    public RestListener(RestExecutor restExecutor) {
+    public RestListener(AgentConfiguration cfg, RestExecutor restExecutor) {
+        this.cfg = cfg;
         this.restExecutor = restExecutor;
     }
 
@@ -42,15 +51,10 @@ public class RestListener extends AbstractListener {
     }
 
     /** {@inheritDoc} */
-    @Override public Object execute(Map<String, Object> args) throws Exception 
{
+    @Override public Object execute(Map<String, Object> args) {
         if (log.isDebugEnabled())
             log.debug("Start parse REST command args: " + args);
 
-        String path = null;
-
-        if (args.containsKey("uri"))
-            path = args.get("uri").toString();
-
         Map<String, Object> params = null;
 
         if (args.containsKey("params"))
@@ -66,11 +70,24 @@ public class RestListener extends AbstractListener {
         if (args.containsKey("headers"))
             headers = (Map<String, Object>)args.get("headers");
 
-        String body = null;
+        try {
+            if (demo) {
+                if (AgentClusterDemo.getDemoUrl() == null) {
+                    AgentClusterDemo.tryStart().await();
+
+                    if (AgentClusterDemo.getDemoUrl() == null)
+                        return RestResult.fail(404, "Failed to send request 
because of embedded node for demo mode is not started yet.");
+                }
+
+                return restExecutor.sendRequest(AgentClusterDemo.getDemoUrl(), 
params, headers);
+            }
 
-        if (args.containsKey("body"))
-            body = args.get("body").toString();
+            return restExecutor.sendRequest(this.cfg.nodeURIs(), params, 
headers);
+        }
+        catch (Exception e) {
+            U.error(log, "Failed to execute REST command with parameters: " + 
params, e);
 
-        return restExecutor.execute(demo, path, params, headers, body);
+            return RestResult.fail(404, e.getMessage());
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
index f2892a6..bb06c32 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
@@ -20,14 +20,11 @@ package org.apache.ignite.console.agent.rest;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.net.ConnectException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
@@ -39,24 +36,20 @@ import 
com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import okhttp3.Dispatcher;
 import okhttp3.FormBody;
 import okhttp3.HttpUrl;
-import okhttp3.MediaType;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
-import okhttp3.RequestBody;
 import okhttp3.Response;
 import org.apache.ignite.IgniteLogger;
-import org.apache.ignite.console.demo.AgentClusterDemo;
 import 
org.apache.ignite.internal.processors.rest.protocols.http.jetty.GridJettyObjectMapper;
-import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.LT;
 import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.lang.IgniteProductVersion;
 import org.apache.ignite.logger.slf4j.Slf4jLogger;
 import org.slf4j.LoggerFactory;
 
 import static com.fasterxml.jackson.core.JsonToken.END_ARRAY;
 import static com.fasterxml.jackson.core.JsonToken.END_OBJECT;
 import static com.fasterxml.jackson.core.JsonToken.START_ARRAY;
+import static org.apache.ignite.console.agent.AgentUtils.trustManager;
 import static 
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_AUTH_FAILED;
 import static 
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_FAILED;
 import static 
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_SUCCESS;
@@ -64,19 +57,7 @@ import static 
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS
 /**
  * API to translate REST requests to Ignite cluster.
  */
-public class RestExecutor {
-    /** */
-    private static final IgniteProductVersion IGNITE_2_1 = 
IgniteProductVersion.fromString("2.1.0");
-
-    /** */
-    private static final IgniteProductVersion IGNITE_2_3 = 
IgniteProductVersion.fromString("2.3.0");
-
-    /** Unique Visor key to get events last order. */
-    private static final String EVT_LAST_ORDER_KEY = "WEB_AGENT_" + 
UUID.randomUUID().toString();
-
-    /** Unique Visor key to get events throttle counter. */
-    private static final String EVT_THROTTLE_CNTR_KEY = "WEB_AGENT_" + 
UUID.randomUUID().toString();
-
+public class RestExecutor implements AutoCloseable {
     /** */
     private static final IgniteLogger log = new 
Slf4jLogger(LoggerFactory.getLogger(RestExecutor.class));
 
@@ -86,33 +67,45 @@ public class RestExecutor {
     /** */
     private final OkHttpClient httpClient;
 
-    /** Node URLs. */
-    private Set<String> nodeUrls = new LinkedHashSet<>();
-
-    /** Latest alive node URL. */
-    private volatile String latestNodeUrl;
+    /** Index of alive node URI. */
+    private Map<List<String>, Integer> startIdxs = U.newHashMap(2);
 
     /**
      * Default constructor.
      */
-    public RestExecutor(String nodeUrl) {
-        Collections.addAll(nodeUrls, nodeUrl.split(","));
-
+    public RestExecutor() {
         Dispatcher dispatcher = new Dispatcher();
         
         dispatcher.setMaxRequests(Integer.MAX_VALUE);
         dispatcher.setMaxRequestsPerHost(Integer.MAX_VALUE);
 
-        httpClient = new OkHttpClient.Builder()
+        OkHttpClient.Builder builder = new OkHttpClient.Builder()
             .readTimeout(0, TimeUnit.MILLISECONDS)
-            .dispatcher(dispatcher)
-            .build();
+            .dispatcher(dispatcher);
+
+        // Workaround for use self-signed certificate
+        if (Boolean.getBoolean("trust.all")) {
+            try {
+                SSLContext ctx = SSLContext.getInstance("TLS");
+
+                // Create an SSLContext that uses our TrustManager
+                ctx.init(null, new TrustManager[] {trustManager()}, null);
+
+                builder.sslSocketFactory(ctx.getSocketFactory(), 
trustManager());
+
+                builder.hostnameVerifier((hostname, session) -> true);
+            } catch (Exception ignored) {
+                LT.warn(log, "Failed to initialize the Trust Manager for 
\"-Dtrust.all\" option to skip certificate validation.");
+            }
+        }
+
+        httpClient = builder.build();
     }
 
     /**
      * Stop HTTP client.
      */
-    public void stop() {
+    @Override public void close() {
         if (httpClient != null) {
             httpClient.dispatcher().executorService().shutdown();
 
@@ -121,28 +114,37 @@ public class RestExecutor {
     }
 
     /** */
-    private RestResult sendRequest0(String nodeUrl, boolean demo, String path, 
Map<String, Object> params,
-        Map<String, Object> headers, String body) throws IOException {
-        if (demo && AgentClusterDemo.getDemoUrl() == null) {
-            try {
-                AgentClusterDemo.tryStart().await();
-            }
-            catch (InterruptedException ignore) {
-                throw new IllegalStateException("Failed to send request 
because of embedded node for demo mode is not started yet.");
+    private RestResult parseResponse(Response res) throws IOException {
+        if (res.isSuccessful()) {
+            RestResponseHolder holder = 
MAPPER.readValue(res.body().byteStream(), RestResponseHolder.class);
+
+            int status = holder.getSuccessStatus();
+
+            switch (status) {
+                case STATUS_SUCCESS:
+                    return RestResult.success(holder.getResponse(), 
holder.getSessionToken());
+
+                default:
+                    return RestResult.fail(status, holder.getError());
             }
         }
 
-        String url = demo ? AgentClusterDemo.getDemoUrl() : nodeUrl;
+        if (res.code() == 401)
+            return RestResult.fail(STATUS_AUTH_FAILED, "Failed to authenticate 
in cluster. " +
+                "Please check agent\'s login and password or node port.");
 
-        HttpUrl httpUrl = HttpUrl.parse(url);
+        if (res.code() == 404)
+            return RestResult.fail(STATUS_FAILED, "Failed connect to 
cluster.");
 
-        if (httpUrl == null)
-            throw new IllegalStateException("Failed to send request because of 
node URL is invalid: " + url);
+        return RestResult.fail(STATUS_FAILED, "Failed to execute REST command: 
" + res.message());
+    }
 
-        HttpUrl.Builder urlBuilder = httpUrl.newBuilder();
+    /** */
+    private RestResult sendRequest(String url, Map<String, Object> params, 
Map<String, Object> headers) throws IOException {
+        HttpUrl httpUrl = HttpUrl.parse(url);
 
-        if (path != null)
-            urlBuilder.addPathSegment(path);
+        HttpUrl.Builder urlBuilder = httpUrl.newBuilder()
+            .addPathSegment("ignite");
 
         final Request.Builder reqBuilder = new Request.Builder();
 
@@ -152,199 +154,51 @@ public class RestExecutor {
                     reqBuilder.addHeader(entry.getKey(), 
entry.getValue().toString());
         }
 
-        if (body != null) {
-            MediaType contentType = MediaType.parse("text/plain");
-
-            reqBuilder.post(RequestBody.create(contentType, body));
-        }
-        else {
-            FormBody.Builder formBody = new FormBody.Builder();
+        FormBody.Builder bodyParams = new FormBody.Builder();
 
-            if (params != null) {
-                for (Map.Entry<String, Object> entry : params.entrySet()) {
-                    if (entry.getValue() != null)
-                        formBody.add(entry.getKey(), 
entry.getValue().toString());
-                }
+        if (params != null) {
+            for (Map.Entry<String, Object> entry : params.entrySet()) {
+                if (entry.getValue() != null)
+                    bodyParams.add(entry.getKey(), 
entry.getValue().toString());
             }
-
-            reqBuilder.post(formBody.build());
         }
 
-        reqBuilder.url(urlBuilder.build());
+        reqBuilder.url(urlBuilder.build())
+            .post(bodyParams.build());
 
         try (Response resp = httpClient.newCall(reqBuilder.build()).execute()) 
{
-            if (resp.isSuccessful()) {
-                RestResponseHolder res = 
MAPPER.readValue(resp.body().byteStream(), RestResponseHolder.class);
-
-                int status = res.getSuccessStatus();
-
-                switch (status) {
-                    case STATUS_SUCCESS:
-                        return RestResult.success(res.getResponse());
-
-                    default:
-                        return RestResult.fail(status, res.getError());
-                }
-            }
-
-            if (resp.code() == 401)
-                return RestResult.fail(STATUS_AUTH_FAILED, "Failed to 
authenticate in cluster. " +
-                    "Please check agent\'s login and password or node port.");
-
-            if (resp.code() == 404)
-                return RestResult.fail(STATUS_FAILED, "Failed connect to 
cluster.");
-
-            return RestResult.fail(STATUS_FAILED, "Failed to execute REST 
command: " + resp.message());
+            return parseResponse(resp);
         }
     }
 
-    /**
-     * Send request to cluster.
-     *
-     * @param demo {@code true} If demo mode.
-     * @param path Request path.
-     * @param params Request params.
-     * @param headers Request headers.
-     * @param body Request body.
-     * @return Request result.
-     * @throws IOException If failed to process request.
-     */
-    private RestResult sendRequest(boolean demo, String path, Map<String, 
Object> params,
-        Map<String, Object> headers, String body) throws IOException {
-        String url = latestNodeUrl;
-
-        try {
-            if (F.isEmpty(url)) {
-                Iterator<String> it = nodeUrls.iterator();
-
-                while (it.hasNext()) {
-                    String nodeUrl = it.next();
-
-                    try {
-                        RestResult res = sendRequest0(nodeUrl, demo, path, 
params, headers, body);
+    /** */
+    public RestResult sendRequest(List<String> nodeURIs, Map<String, Object> 
params, Map<String, Object> headers) throws IOException {
+        Integer startIdx = startIdxs.getOrDefault(nodeURIs, 0);
 
-                        log.info("Connected to cluster [url=" + nodeUrl + "]");
+        for (int i = 0;  i < nodeURIs.size(); i++) {
+            Integer currIdx = (startIdx + i) % nodeURIs.size();
 
-                        latestNodeUrl = nodeUrl;
+            String nodeUrl = nodeURIs.get(currIdx);
 
-                        return res;
-                    }
-                    catch (ConnectException ignored) {
-                        String msg = "Failed connect to cluster [url=" + 
nodeUrl + ", parameters=" + params + "]";
+            try {
+                RestResult res = sendRequest(nodeUrl, params, headers);
 
-                        LT.warn(log, msg);
+                LT.info(log, "Connected to cluster [url=" + nodeUrl + "]");
 
-                        if (!it.hasNext())
-                            throw new ConnectException(msg);
-                    }
-                }
+                startIdxs.put(nodeURIs, currIdx);
 
-                throw new ConnectException("Failed connect to cluster [urls=" 
+ nodeUrls + ", parameters=" + params + "]");
+                return res;
             }
-            else {
-                try {
-                    return sendRequest0(url, demo, path, params, headers, 
body);
-                }
-                catch (ConnectException e) {
-                    latestNodeUrl = null;
-
-                    if (nodeUrls.size() > 1)
-                        return sendRequest(demo, path, params, headers, body);
-
-                    throw e;
-                }
-            }        }
-        catch (ConnectException ce) {
-            LT.warn(log, "Failed connect to cluster. " +
-                "Please ensure that nodes have [ignite-rest-http] module in 
classpath " +
-                "(was copied from libs/optional to libs folder).");
-
-            throw ce;
-        }
-    }
-
-    /**
-     * @param demo Is demo node request.
-     * @param path Path segment.
-     * @param params Params.
-     * @param headers Headers.
-     * @param body Body.
-     */
-    public RestResult execute(boolean demo, String path, Map<String, Object> 
params,
-        Map<String, Object> headers, String body) {
-        if (log.isDebugEnabled())
-            log.debug("Start execute REST command [uri=/" + (path == null ? "" 
: path) +
-                ", parameters=" + params + "]");
-
-        try {
-            return sendRequest(demo, path, params, headers, body);
-        }
-        catch (Exception e) {
-            U.error(log, "Failed to execute REST command [uri=/" + (path == 
null ? "" : path) +
-                ", parameters=" + params + "]", e);
-
-            return RestResult.fail(404, e.getMessage());
-        }
-    }
-
-    /**
-     * @param demo {@code true} in case of demo mode.
-     * @param full Flag indicating whether to collect metrics or not.
-     * @throws IOException If failed to collect topology.
-     */
-    public RestResult topology(boolean demo, boolean full) throws IOException {
-        Map<String, Object> params = new HashMap<>(3);
-
-        params.put("cmd", "top");
-        params.put("attr", true);
-        params.put("mtr", full);
-
-        return sendRequest(demo, "ignite", params, null, null);
-    }
-
-    /**
-     * @param ver Cluster version.
-     * @param nid Node ID.
-     * @return Cluster active state.
-     * @throws IOException If failed to collect cluster active state.
-     */
-    public boolean active(IgniteProductVersion ver, UUID nid) throws 
IOException {
-        Map<String, Object> params = new HashMap<>();
-
-        boolean v23 = ver.compareTo(IGNITE_2_3) >= 0;
-
-        if (v23)
-            params.put("cmd", "currentState");
-        else {
-            params.put("cmd", "exe");
-            params.put("name", 
"org.apache.ignite.internal.visor.compute.VisorGatewayTask");
-            params.put("p1", nid);
-            params.put("p2", 
"org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTask");
-            params.put("p3", 
"org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTaskArg");
-            params.put("p4", false);
-            params.put("p5", EVT_LAST_ORDER_KEY);
-            params.put("p6", EVT_THROTTLE_CNTR_KEY);
-
-            if (ver.compareTo(IGNITE_2_1) >= 0)
-                params.put("p7", false);
-            else {
-                params.put("p7", 10);
-                params.put("p8", false);
+            catch (ConnectException ignored) {
+                // No-op.
             }
         }
 
-        RestResult res = sendRequest(false, "ignite", params, null, null);
-
-        switch (res.getStatus()) {
-            case STATUS_SUCCESS:
-                if (v23)
-                    return Boolean.valueOf(res.getData());
+        LT.warn(log, "Failed connect to cluster. " +
+            "Please ensure that nodes have [ignite-rest-http] module in 
classpath " +
+            "(was copied from libs/optional to libs folder).");
 
-                return res.getData().contains("\"active\":true");
-
-            default:
-                throw new IOException(res.getError());
-        }
+        throw new ConnectException("Failed connect to cluster [urls=" + 
nodeURIs + ", parameters=" + params + "]");
     }
 
     /**
@@ -361,7 +215,7 @@ public class RestExecutor {
         private String res;
 
         /** Session token string representation. */
-        private String sesTokStr;
+        private String sesTok;
 
         /**
          * @return {@code True} if this request was successful.
@@ -410,14 +264,14 @@ public class RestExecutor {
          * @return String representation of session token.
          */
         public String getSessionToken() {
-            return sesTokStr;
+            return sesTok;
         }
 
         /**
          * @param sesTokStr String representation of session token.
          */
         public void setSessionToken(String sesTokStr) {
-            this.sesTokStr = sesTokStr;
+            this.sesTok = sesTokStr;
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java
index 962ffb6..fc8b4e9 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java
@@ -30,6 +30,9 @@ public class RestResult {
     /** The field contains result of command. */
     private String data;
 
+    /** Session token string representation. */
+    private String sesTok;
+
     /** Flag of zipped data. */
     private boolean zipped;
 
@@ -57,8 +60,12 @@ public class RestResult {
      * @param data The field contains result of command.
      * @return Request result.
      */
-    public static RestResult success(String data) {
-        return new RestResult(0, null, data);
+    public static RestResult success(String data, String sesTok) {
+        RestResult res = new RestResult(0, null, data);
+
+        res.sesTok = sesTok;
+
+        return res;
     }
 
     /**
@@ -83,6 +90,13 @@ public class RestResult {
     }
 
     /**
+     * @return String representation of session token.
+     */
+    public String getSessionToken() {
+        return sesTok;
+    }
+
+    /**
      * @param data Set zipped data.
      */
     public void zipData(String data) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/523a871b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
index 1fd286c..2555ee1 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
@@ -21,6 +21,7 @@ import java.io.File;
 import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -100,7 +101,7 @@ public class AgentClusterDemo {
     private static CountDownLatch initLatch = new CountDownLatch(1);
 
     /** */
-    private static volatile String demoUrl;
+    private static volatile List<String> demoUrl;
 
     /**
      * Configure node.
@@ -204,7 +205,7 @@ public class AgentClusterDemo {
     }
 
     /** */
-    public static String getDemoUrl() {
+    public static List<String> getDemoUrl() {
         return demoUrl;
     }
 
@@ -269,7 +270,7 @@ public class AgentClusterDemo {
 
                             log.info("DEMO: Started embedded node for demo 
purpose [TCP binary port={}, Jetty REST port={}]", port, jettyPort);
 
-                            demoUrl = String.format("http://%s:%d";, jettyHost, 
jettyPort);
+                            demoUrl = 
Collections.singletonList(String.format("http://%s:%d";, jettyHost, jettyPort));
 
                             initLatch.countDown();
                         }

Reply via email to