You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2017/02/13 12:31:41 UTC

[08/12] ignite git commit: IGNITE-4687 Added pool to process REST request in Web Agent.

IGNITE-4687 Added pool to process REST request in Web Agent.


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

Branch: refs/heads/ignite-4003
Commit: 262a3410802de790b75de9b94fea9599b30171bd
Parents: 0ace63c
Author: Andrey Novikov <an...@gridgain.com>
Authored: Mon Feb 13 17:35:29 2017 +0700
Committer: Andrey Novikov <an...@gridgain.com>
Committed: Mon Feb 13 17:35:29 2017 +0700

----------------------------------------------------------------------
 .../ignite/console/agent/AgentLauncher.java     | 203 ++++++------
 .../apache/ignite/console/agent/AgentUtils.java |  80 +++++
 .../console/agent/handlers/AbstractHandler.java | 110 -------
 .../agent/handlers/AbstractListener.java        | 104 ++++++
 .../console/agent/handlers/DatabaseHandler.java | 298 -----------------
 .../agent/handlers/DatabaseListener.java        | 316 +++++++++++++++++++
 .../console/agent/handlers/RestHandler.java     | 276 ----------------
 .../console/agent/handlers/RestListener.java    | 280 ++++++++++++++++
 8 files changed, 880 insertions(+), 787 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/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 049791f..a3d609f 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
@@ -41,8 +41,8 @@ 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.DatabaseHandler;
-import org.apache.ignite.console.agent.handlers.RestHandler;
+import org.apache.ignite.console.agent.handlers.DatabaseListener;
+import org.apache.ignite.console.agent.handlers.RestListener;
 import org.apache.ignite.internal.util.typedef.X;
 import org.apache.log4j.Logger;
 import org.json.JSONException;
@@ -278,141 +278,138 @@ public class AgentLauncher {
             cfg.tokens(Arrays.asList(tokens.trim().split(",")));
         }
 
-        final RestHandler restHnd = new RestHandler(cfg);
+        URI uri = URI.create(cfg.serverUri());
 
-        try {
-            restHnd.start();
+        // Create proxy authenticator using passed properties.
+        switch (uri.getScheme()) {
+            case "http":
+            case "https":
+                final String username = System.getProperty(uri.getScheme() + ".proxyUsername");
+                final char[] pwd = System.getProperty(uri.getScheme() +  ".proxyPassword", "").toCharArray();
 
-            URI uri = URI.create(cfg.serverUri());
+                Authenticator.setDefault(new Authenticator() {
+                    @Override protected PasswordAuthentication getPasswordAuthentication() {
+                        return new PasswordAuthentication(username, pwd);
+                    }
+                });
 
-            // Create proxy authenticator using passed properties.
-            switch (uri.getScheme()) {
-                case "http":
-                case "https":
-                    final String username = System.getProperty(uri.getScheme() + ".proxyUsername");
-                    final char[] pwd = System.getProperty(uri.getScheme() +  ".proxyPassword", "").toCharArray();
+                break;
 
-                    Authenticator.setDefault(new Authenticator() {
-                        @Override protected PasswordAuthentication getPasswordAuthentication() {
-                            return new PasswordAuthentication(username, pwd);
-                        }
-                    });
-
-                    break;
+            default:
+                // No-op.
+        }
 
-                default:
-                    // No-op.
-            }
+        IO.Options opts = new IO.Options();
 
-            IO.Options opts = new IO.Options();
+        opts.path = "/agents";
 
-            opts.path = "/agents";
+        // Workaround for use self-signed certificate
+        if (Boolean.getBoolean("trust.all")) {
+            SSLContext ctx = SSLContext.getInstance("TLS");
 
-            // Workaround for use self-signed certificate
-            if (Boolean.getBoolean("trust.all")) {
-                SSLContext ctx = SSLContext.getInstance("TLS");
+            // Create an SSLContext that uses our TrustManager
+            ctx.init(null, getTrustManagers(), null);
 
-                // Create an SSLContext that uses our TrustManager
-                ctx.init(null, getTrustManagers(), null);
+            opts.sslContext = ctx;
+        }
 
-                opts.sslContext = ctx;
-            }
+        final Socket client = IO.socket(uri, opts);
 
-            final Socket client = IO.socket(uri, opts);
+        final RestListener restHnd = new RestListener(cfg);
 
-            try {
-                Emitter.Listener onConnecting = new Emitter.Listener() {
-                    @Override public void call(Object... args) {
-                        log.info("Connecting to: " + cfg.serverUri());
-                    }
-                };
+        final DatabaseListener dbHnd = new DatabaseListener(cfg);
 
-                Emitter.Listener onConnect = new Emitter.Listener() {
-                    @Override public void call(Object... args) {
-                        log.info("Connection established.");
+        try {
+            Emitter.Listener onConnecting = new Emitter.Listener() {
+                @Override public void call(Object... args) {
+                    log.info("Connecting to: " + cfg.serverUri());
+                }
+            };
 
-                        JSONObject authMsg = new JSONObject();
+            Emitter.Listener onConnect = new Emitter.Listener() {
+                @Override public void call(Object... args) {
+                    log.info("Connection established.");
 
-                        try {
-                            authMsg.put("tokens", cfg.tokens());
+                    JSONObject authMsg = new JSONObject();
 
-                            String clsName = AgentLauncher.class.getSimpleName() + ".class";
+                    try {
+                        authMsg.put("tokens", cfg.tokens());
 
-                            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) {
-                                    // Authentication failed if response contains args.
-                                    if (args != null && args.length > 0) {
-                                        onDisconnect.call(args);
+                            authMsg.put("ver", attr.getValue("Implementation-Version"));
+                            authMsg.put("bt", attr.getValue("Build-Time"));
+                        }
 
-                                        System.exit(1);
-                                    }
+                        client.emit("agent:auth", authMsg, new Ack() {
+                            @Override public void call(Object... args) {
+                                // Authentication failed if response contains args.
+                                if (args != null && args.length > 0) {
+                                    onDisconnect.call(args);
 
-                                    log.info("Authentication success.");
+                                    System.exit(1);
                                 }
-                            });
-                        }
-                        catch (JSONException | IOException e) {
-                            log.error("Failed to construct authentication message", e);
 
-                            client.close();
-                        }
+                                log.info("Authentication success.");
+                            }
+                        });
                     }
-                };
-
-                DatabaseHandler dbHnd = new DatabaseHandler(cfg);
-
-                final CountDownLatch latch = new CountDownLatch(1);
-
-                client
-                    .on(EVENT_CONNECTING, onConnecting)
-                    .on(EVENT_CONNECT, onConnect)
-                    .on(EVENT_CONNECT_ERROR, onError)
-                    .on(EVENT_RECONNECTING, onConnecting)
-                    .on(EVENT_NODE_REST, restHnd)
-                    .on(EVENT_SCHEMA_IMPORT_DRIVERS, dbHnd.availableDriversListener())
-                    .on(EVENT_SCHEMA_IMPORT_SCHEMAS, dbHnd.schemasListener())
-                    .on(EVENT_SCHEMA_IMPORT_METADATA, dbHnd.metadataListener())
-                    .on(EVENT_ERROR, onError)
-                    .on(EVENT_DISCONNECT, onDisconnect)
-                    .on(EVENT_AGENT_WARNING, new Emitter.Listener() {
-                        @Override public void call(Object... args) {
-                            log.warn(args[0]);
-                        }
-                    })
-                    .on(EVENT_AGENT_CLOSE, new Emitter.Listener() {
-                        @Override public void call(Object... args) {
-                            onDisconnect.call(args);
+                    catch (JSONException | IOException e) {
+                        log.error("Failed to construct authentication message", e);
+
+                        client.close();
+                    }
+                }
+            };
+
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            client
+                .on(EVENT_CONNECTING, onConnecting)
+                .on(EVENT_CONNECT, onConnect)
+                .on(EVENT_CONNECT_ERROR, onError)
+                .on(EVENT_RECONNECTING, onConnecting)
+                .on(EVENT_NODE_REST, restHnd)
+                .on(EVENT_SCHEMA_IMPORT_DRIVERS, dbHnd.availableDriversListener())
+                .on(EVENT_SCHEMA_IMPORT_SCHEMAS, dbHnd.schemasListener())
+                .on(EVENT_SCHEMA_IMPORT_METADATA, dbHnd.metadataListener())
+                .on(EVENT_ERROR, onError)
+                .on(EVENT_DISCONNECT, onDisconnect)
+                .on(EVENT_AGENT_WARNING, new Emitter.Listener() {
+                    @Override public void call(Object... args) {
+                        log.warn(args[0]);
+                    }
+                })
+                .on(EVENT_AGENT_CLOSE, new Emitter.Listener() {
+                    @Override public void call(Object... args) {
+                        onDisconnect.call(args);
 
-                            client.off();
+                        client.off();
 
-                            latch.countDown();
-                        }
-                    });
+                        latch.countDown();
+                    }
+                });
 
-                client.connect();
+            client.connect();
 
-                latch.await();
-            }
-            finally {
-                client.close();
-            }
+            latch.await();
         }
         finally {
+            client.close();
+
             restHnd.stop();
+
+            dbHnd.stop();
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/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 50a849a..cb22651 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
@@ -17,11 +17,19 @@
 
 package org.apache.ignite.console.agent;
 
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
+import io.socket.client.Ack;
 import java.io.File;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.ProtectionDomain;
+import java.util.Arrays;
 import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONObject;
 
 /**
  * Utility methods.
@@ -30,6 +38,28 @@ public class AgentUtils {
     /** */
     private static final Logger log = Logger.getLogger(AgentUtils.class.getName());
 
+    /** JSON object mapper. */
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+    /** */
+    private static final Ack NOOP_CB = new Ack() {
+        @Override public void call(Object... args) {
+            if (args != null && args.length > 0 && args[0] instanceof Throwable)
+                log.error("Failed to execute request on agent.", (Throwable) args[0]);
+            else
+                log.info("Request on agent successfully executed " + Arrays.toString(args));
+        }
+    };
+
+    static {
+        JsonOrgModule module = new JsonOrgModule();
+
+        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
+        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
+
+        mapper.registerModule(module);
+    }
+    
     /**
      * Default constructor.
      */
@@ -108,4 +138,54 @@ public class AgentUtils {
 
         return null;
     }
+
+    /**
+     * Get callback from handler arguments.
+     *
+     * @param args Arguments.
+     * @return Callback or noop callback.
+     */
+    public static Ack safeCallback(Object[] args) {
+        boolean hasCb = args != null && args.length > 0 && args[args.length - 1] instanceof Ack;
+
+        return hasCb ? (Ack)args[args.length - 1] : NOOP_CB;
+    }
+
+    /**
+     * Remove callback from handler arguments.
+     * 
+     * @param args Arguments.
+     * @return Arguments without callback.
+     */
+    public static Object[] removeCallback(Object[] args) {
+        boolean hasCb = args != null && args.length > 0 && args[args.length - 1] instanceof Ack;
+
+        return hasCb ? Arrays.copyOf(args, args.length - 1) : args;
+    }
+
+    /**
+     * Map java object to JSON object.
+     * 
+     * @param obj Java object.
+     * @return {@link JSONObject} or {@link JSONArray}.
+     * @throws IllegalArgumentException If conversion fails due to incompatible type.
+     */
+    public static Object toJSON(Object obj) {
+        if (obj instanceof Iterable)
+            return mapper.convertValue(obj, JSONArray.class);
+
+        return mapper.convertValue(obj, JSONObject.class);
+    }
+
+    /**
+     * Map JSON object to java object.
+     *
+     * @param obj {@link JSONObject} or {@link JSONArray}.
+     * @param toValType Expected value type.
+     * @return Mapped object type of {@link T}.
+     * @throws IllegalArgumentException If conversion fails due to incompatible type.
+     */
+    public static <T> T fromJSON(Object obj, Class<T> toValType) throws IllegalArgumentException {
+        return mapper.convertValue(obj, toValType);
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java
deleted file mode 100644
index 7e4e320..0000000
--- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java
+++ /dev/null
@@ -1,110 +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 com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
-import io.socket.client.Ack;
-import io.socket.emitter.Emitter;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Map;
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-/**
- * Base class for web socket handlers.
- */
-abstract class AbstractHandler implements Emitter.Listener {
-    /** JSON object mapper. */
-    private static final ObjectMapper mapper = new ObjectMapper();
-
-    static {
-        JsonOrgModule module = new JsonOrgModule();
-
-        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
-        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
-
-        mapper.registerModule(module);
-    }
-
-    /**
-     * @param obj Object.
-     * @return {@link JSONObject} or {@link JSONArray}.
-     */
-    private Object toJSON(Object obj) {
-        if (obj instanceof Iterable)
-            return mapper.convertValue(obj, JSONArray.class);
-
-        return mapper.convertValue(obj, JSONObject.class);
-    }
-
-    /** {@inheritDoc} */
-    @SuppressWarnings("unchecked")
-    @Override public final void call(Object... args) {
-        Ack cb = null;
-
-        try {
-            if (args == null || args.length == 0)
-                throw new IllegalArgumentException("Missing arguments.");
-
-            if (args.length > 2)
-                throw new IllegalArgumentException("Wrong arguments count, must be <= 2: " + Arrays.toString(args));
-
-            JSONObject lsnrArgs = null;
-
-            if (args.length == 1) {
-                if (args[0] instanceof JSONObject)
-                    lsnrArgs = (JSONObject)args[0];
-                else if (args[0] instanceof Ack)
-                    cb = (Ack)args[0];
-                else
-                    throw new IllegalArgumentException("Wrong type of argument, must be JSONObject or Ack: " + args[0]);
-            }
-            else {
-                if (args[0] != null && !(args[0] instanceof JSONObject))
-                    throw new IllegalArgumentException("Wrong type of argument, must be JSONObject: " + args[0]);
-
-                if (!(args[1] instanceof Ack))
-                    throw new IllegalArgumentException("Wrong type of argument, must be Ack: " + args[1]);
-
-                lsnrArgs = (JSONObject)args[0];
-
-                cb = (Ack)args[1];
-            }
-
-            Object res = execute(lsnrArgs == null ? Collections.emptyMap() : mapper.convertValue(lsnrArgs, Map.class));
-
-            if (cb != null)
-                cb.call(null, toJSON(res));
-        }
-        catch (Exception e) {
-            if (cb != null)
-                cb.call(e, null);
-        }
-    }
-
-    /**
-     * Execute command with specified arguments.
-     *
-     * @param args Map with method args.
-     */
-    public abstract Object execute(Map<String, Object> args) throws Exception;
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/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
new file mode 100644
index 0000000..987dac9
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java
@@ -0,0 +1,104 @@
+/*
+ * 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.emitter.Emitter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.log4j.Logger;
+
+import static org.apache.ignite.console.agent.AgentUtils.removeCallback;
+import static org.apache.ignite.console.agent.AgentUtils.fromJSON;
+import static org.apache.ignite.console.agent.AgentUtils.safeCallback;
+import static org.apache.ignite.console.agent.AgentUtils.toJSON;
+
+/**
+ * Base class for web socket handlers.
+ */
+abstract class AbstractListener implements Emitter.Listener {
+    /** */
+    private ExecutorService pool;
+
+    /** */
+    final Logger log = Logger.getLogger(this.getClass().getName());
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public final void call(Object... args) {
+        final Ack cb = safeCallback(args);
+
+        args = removeCallback(args);
+
+        try {
+            final Map<String, Object> params;
+
+            if (args == null || args.length == 0)
+                params = Collections.emptyMap();
+            else if (args.length == 1)
+                params = fromJSON(args[0], Map.class);
+            else
+                throw new IllegalArgumentException("Wrong arguments count, must be <= 1: " + Arrays.toString(args));
+
+            if (pool == null)
+                pool = newThreadPool();
+
+            pool.submit(new Runnable() {
+                @Override public void run() {
+                    try {
+                        Object res = execute(params);
+
+                        cb.call(null, toJSON(res));
+                    } catch (Exception e) {
+                        cb.call(e, null);
+                    }
+                }
+            });
+        }
+        catch (Exception e) {
+            cb.call(e, null);
+        }
+    }
+
+    /**
+     * Stop handler.
+     */
+    public void stop() {
+        if (pool != null)
+            pool.shutdownNow();
+    }
+
+    /**
+     * Creates a thread pool that can schedule commands to run after a given delay, or to execute periodically.
+     *
+     * @return Newly created thread pool.
+     */
+    protected ExecutorService newThreadPool() {
+        return Executors.newSingleThreadExecutor();
+    }
+
+    /**
+     * Execute command with specified arguments.
+     *
+     * @param args Map with method args.
+     */
+    public abstract Object execute(Map<String, Object> args) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java
deleted file mode 100644
index 02146d9..0000000
--- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java
+++ /dev/null
@@ -1,298 +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.emitter.Emitter;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import org.apache.ignite.console.agent.AgentConfiguration;
-import org.apache.ignite.console.demo.AgentMetadataDemo;
-import org.apache.ignite.schema.parser.DbMetadataReader;
-import org.apache.ignite.schema.parser.DbTable;
-import org.apache.log4j.Logger;
-
-import static org.apache.ignite.console.agent.AgentUtils.resolvePath;
-
-/**
- * API to extract database metadata.
- */
-public class DatabaseHandler {
-    /** */
-    private static final Logger log = Logger.getLogger(DatabaseHandler.class.getName());
-
-    /** */
-    private final File driversFolder;
-
-    /**
-     * @param cfg Config.
-     */
-    public DatabaseHandler(AgentConfiguration cfg) {
-        driversFolder = resolvePath(cfg.driversFolder() == null ? "jdbc-drivers" : cfg.driversFolder());
-    }
-
-    /**
-     * @param jdbcDriverJarPath JDBC driver JAR path.
-     * @param jdbcDriverCls JDBC driver class.
-     * @param jdbcUrl JDBC URL.
-     * @param jdbcInfo Properties to connect to database.
-     * @return Connection to database.
-     * @throws SQLException
-     */
-    private Connection connect(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
-        Properties jdbcInfo) throws SQLException {
-        if (AgentMetadataDemo.isTestDriveUrl(jdbcUrl))
-            return AgentMetadataDemo.testDrive();
-
-        if (!new File(jdbcDriverJarPath).isAbsolute() && driversFolder != null)
-            jdbcDriverJarPath = new File(driversFolder, jdbcDriverJarPath).getPath();
-
-        return DbMetadataReader.getInstance().connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo);
-    }
-
-    /**
-     * @param jdbcDriverJarPath JDBC driver JAR path.
-     * @param jdbcDriverCls JDBC driver class.
-     * @param jdbcUrl JDBC URL.
-     * @param jdbcInfo Properties to connect to database.
-     * @return Collection of schema names.
-     * @throws SQLException
-     */
-    protected Collection<String> schemas(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
-        Properties jdbcInfo) throws SQLException {
-        if (log.isDebugEnabled())
-            log.debug("Start collecting database schemas [drvJar=" + jdbcDriverJarPath +
-                ", drvCls=" + jdbcDriverCls + ", jdbcUrl=" + jdbcUrl + "]");
-
-        try (Connection conn = connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo)) {
-            Collection<String> schemas = DbMetadataReader.getInstance().schemas(conn);
-
-            if (log.isDebugEnabled())
-                log.debug("Finished collection of schemas [jdbcUrl=" + jdbcUrl + ", count=" + schemas.size() + "]");
-
-            return schemas;
-        }
-        catch (Throwable e) {
-            log.error("Failed to collect schemas", e);
-
-            throw new SQLException("Failed to collect schemas", e);
-        }
-    }
-
-    /**
-     * Listener for schema names.
-     *
-     * @return Collection of schema names.
-     */
-    public Emitter.Listener schemasListener() {
-        return new AbstractHandler() {
-            @Override public Object execute(Map<String, Object> args) throws Exception {
-                String driverPath = null;
-
-                if (args.containsKey("driverPath"))
-                    driverPath = args.get("driverPath").toString();
-
-                if (!args.containsKey("driverClass"))
-                    throw new IllegalArgumentException("Missing driverClass in arguments: " + args);
-
-                String driverCls = args.get("driverClass").toString();
-
-                if (!args.containsKey("url"))
-                    throw new IllegalArgumentException("Missing url in arguments: " + args);
-
-                String url = args.get("url").toString();
-
-                if (!args.containsKey("info"))
-                    throw new IllegalArgumentException("Missing info in arguments: " + args);
-
-                Properties info = new Properties();
-
-                info.putAll((Map)args.get("info"));
-
-                return schemas(driverPath, driverCls, url, info);
-            }
-        };
-    }
-
-    /**
-     * @param jdbcDriverJarPath JDBC driver JAR path.
-     * @param jdbcDriverCls JDBC driver class.
-     * @param jdbcUrl JDBC URL.
-     * @param jdbcInfo Properties to connect to database.
-     * @param schemas List of schema names to process.
-     * @param tblsOnly If {@code true} then only tables will be processed otherwise views also will be processed.
-     * @return Collection of tables.
-     */
-    protected Collection<DbTable> metadata(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
-        Properties jdbcInfo, List<String> schemas, boolean tblsOnly) throws SQLException {
-        if (log.isDebugEnabled())
-            log.debug("Start collecting database metadata [drvJar=" + jdbcDriverJarPath +
-                ", drvCls=" + jdbcDriverCls + ", jdbcUrl=" + jdbcUrl + "]");
-
-        try (Connection conn = connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo)) {
-            Collection<DbTable> metadata = DbMetadataReader.getInstance().metadata(conn, schemas, tblsOnly);
-
-            if (log.isDebugEnabled())
-                log.debug("Finished collection of metadata [jdbcUrl=" + jdbcUrl + ", count=" + metadata.size() + "]");
-
-            return metadata;
-        }
-        catch (Throwable e) {
-            log.error("Failed to collect metadata", e);
-
-            throw new SQLException("Failed to collect metadata", e);
-        }
-    }
-
-    /**
-     * Listener for tables.
-     *
-     * @return Collection of tables.
-     */
-    public Emitter.Listener metadataListener() {
-        return new AbstractHandler() {
-            @SuppressWarnings("unchecked")
-            @Override public Object execute(Map<String, Object> args) throws Exception {
-                String driverPath = null;
-
-                if (args.containsKey("driverPath"))
-                    driverPath = args.get("driverPath").toString();
-
-                if (!args.containsKey("driverClass"))
-                    throw new IllegalArgumentException("Missing driverClass in arguments: " + args);
-
-                String driverCls = args.get("driverClass").toString();
-
-                if (!args.containsKey("url"))
-                    throw new IllegalArgumentException("Missing url in arguments: " + args);
-
-                String url = args.get("url").toString();
-
-                if (!args.containsKey("info"))
-                    throw new IllegalArgumentException("Missing info in arguments: " + args);
-
-                Properties info = new Properties();
-
-                info.putAll((Map)args.get("info"));
-
-                if (!args.containsKey("schemas"))
-                    throw new IllegalArgumentException("Missing schemas in arguments: " + args);
-
-                List<String> schemas = (List<String>)args.get("schemas");
-
-                if (!args.containsKey("tablesOnly"))
-                    throw new IllegalArgumentException("Missing tablesOnly in arguments: " + args);
-
-                boolean tblsOnly = (boolean)args.get("tablesOnly");
-
-                return metadata(driverPath, driverCls, url, info, schemas, tblsOnly);
-            }
-        };
-    }
-
-    /**
-     * Listener for drivers.
-     *
-     * @return Drivers in drivers folder
-     * @see AgentConfiguration#driversFolder
-     */
-    public Emitter.Listener availableDriversListener() {
-        return new AbstractHandler() {
-            @Override public Object execute(Map<String, Object> args) throws Exception {
-                if (driversFolder == null) {
-                    log.info("JDBC drivers folder not specified, returning empty list");
-
-                    return Collections.emptyList();
-                }
-
-                if (log.isDebugEnabled())
-                    log.debug("Collecting JDBC drivers in folder: " + driversFolder.getPath());
-
-                File[] list = driversFolder.listFiles(new FilenameFilter() {
-                    @Override public boolean accept(File dir, String name) {
-                        return name.endsWith(".jar");
-                    }
-                });
-
-                if (list == null) {
-                    log.info("JDBC drivers folder has no files, returning empty list");
-
-                    return Collections.emptyList();
-                }
-
-                List<JdbcDriver> res = new ArrayList<>();
-
-                for (File file : list) {
-                    try {
-                        boolean win = System.getProperty("os.name").contains("win");
-
-                        URL url = new URL("jar", null,
-                            "file:" + (win ? "/" : "") + file.getPath() + "!/META-INF/services/java.sql.Driver");
-
-                        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
-                            String jdbcDriverCls = reader.readLine();
-
-                            res.add(new JdbcDriver(file.getName(), jdbcDriverCls));
-
-                            if (log.isDebugEnabled())
-                                log.debug("Found: [driver=" + file + ", class=" + jdbcDriverCls + "]");
-                        }
-                    }
-                    catch (IOException e) {
-                        res.add(new JdbcDriver(file.getName(), null));
-
-                        log.info("Found: [driver=" + file + "]");
-                        log.info("Failed to detect driver class: " + e.getMessage());
-                    }
-                }
-
-                return res;
-            }
-        };
-    }
-
-    /**
-     * Wrapper class for later to be transformed to JSON and send to Web Console.
-     */
-    private static class JdbcDriver {
-        /** */
-        public final String jdbcDriverJar;
-        /** */
-        public final String jdbcDriverCls;
-
-        /**
-         * @param jdbcDriverJar File name of driver jar file.
-         * @param jdbcDriverCls Optional JDBC driver class.
-         */
-        public JdbcDriver(String jdbcDriverJar, String jdbcDriverCls) {
-            this.jdbcDriverJar = jdbcDriverJar;
-            this.jdbcDriverCls = jdbcDriverCls;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/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
new file mode 100644
index 0000000..4577228
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java
@@ -0,0 +1,316 @@
+/*
+ * 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.emitter.Emitter;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.apache.ignite.console.agent.AgentConfiguration;
+import org.apache.ignite.console.demo.AgentMetadataDemo;
+import org.apache.ignite.schema.parser.DbMetadataReader;
+import org.apache.ignite.schema.parser.DbTable;
+import org.apache.log4j.Logger;
+
+import static org.apache.ignite.console.agent.AgentUtils.resolvePath;
+
+/**
+ * API to extract database metadata.
+ */
+public class DatabaseListener {
+    /** */
+    private static final Logger log = Logger.getLogger(DatabaseListener.class.getName());
+
+    /** */
+    private final File driversFolder;
+
+    /** */
+    private final AbstractListener schemasLsnr = new AbstractListener() {
+        @Override public Object execute(Map<String, Object> args) throws Exception {
+            String driverPath = null;
+
+            if (args.containsKey("driverPath"))
+                driverPath = args.get("driverPath").toString();
+
+            if (!args.containsKey("driverClass"))
+                throw new IllegalArgumentException("Missing driverClass in arguments: " + args);
+
+            String driverCls = args.get("driverClass").toString();
+
+            if (!args.containsKey("url"))
+                throw new IllegalArgumentException("Missing url in arguments: " + args);
+
+            String url = args.get("url").toString();
+
+            if (!args.containsKey("info"))
+                throw new IllegalArgumentException("Missing info in arguments: " + args);
+
+            Properties info = new Properties();
+
+            info.putAll((Map)args.get("info"));
+
+            return schemas(driverPath, driverCls, url, info);
+        }
+    };
+
+    private final AbstractListener metadataLsnr = new AbstractListener() {
+        @SuppressWarnings("unchecked")
+        @Override public Object execute(Map<String, Object> args) throws Exception {
+            String driverPath = null;
+
+            if (args.containsKey("driverPath"))
+                driverPath = args.get("driverPath").toString();
+
+            if (!args.containsKey("driverClass"))
+                throw new IllegalArgumentException("Missing driverClass in arguments: " + args);
+
+            String driverCls = args.get("driverClass").toString();
+
+            if (!args.containsKey("url"))
+                throw new IllegalArgumentException("Missing url in arguments: " + args);
+
+            String url = args.get("url").toString();
+
+            if (!args.containsKey("info"))
+                throw new IllegalArgumentException("Missing info in arguments: " + args);
+
+            Properties info = new Properties();
+
+            info.putAll((Map)args.get("info"));
+
+            if (!args.containsKey("schemas"))
+                throw new IllegalArgumentException("Missing schemas in arguments: " + args);
+
+            List<String> schemas = (List<String>)args.get("schemas");
+
+            if (!args.containsKey("tablesOnly"))
+                throw new IllegalArgumentException("Missing tablesOnly in arguments: " + args);
+
+            boolean tblsOnly = (boolean)args.get("tablesOnly");
+
+            return metadata(driverPath, driverCls, url, info, schemas, tblsOnly);
+        }
+    };
+
+    private final AbstractListener availableDriversLsnr = new AbstractListener() {
+        @Override public Object execute(Map<String, Object> args) throws Exception {
+            if (driversFolder == null) {
+                log.info("JDBC drivers folder not specified, returning empty list");
+
+                return Collections.emptyList();
+            }
+
+            if (log.isDebugEnabled())
+                log.debug("Collecting JDBC drivers in folder: " + driversFolder.getPath());
+
+            File[] list = driversFolder.listFiles(new FilenameFilter() {
+                @Override public boolean accept(File dir, String name) {
+                    return name.endsWith(".jar");
+                }
+            });
+
+            if (list == null) {
+                log.info("JDBC drivers folder has no files, returning empty list");
+
+                return Collections.emptyList();
+            }
+
+            List<JdbcDriver> res = new ArrayList<>();
+
+            for (File file : list) {
+                try {
+                    boolean win = System.getProperty("os.name").contains("win");
+
+                    URL url = new URL("jar", null,
+                        "file:" + (win ? "/" : "") + file.getPath() + "!/META-INF/services/java.sql.Driver");
+
+                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
+                        String jdbcDriverCls = reader.readLine();
+
+                        res.add(new JdbcDriver(file.getName(), jdbcDriverCls));
+
+                        if (log.isDebugEnabled())
+                            log.debug("Found: [driver=" + file + ", class=" + jdbcDriverCls + "]");
+                    }
+                }
+                catch (IOException e) {
+                    res.add(new JdbcDriver(file.getName(), null));
+
+                    log.info("Found: [driver=" + file + "]");
+                    log.info("Failed to detect driver class: " + e.getMessage());
+                }
+            }
+
+            return res;
+        }
+    };
+
+    /**
+     * @param cfg Config.
+     */
+    public DatabaseListener(AgentConfiguration cfg) {
+        driversFolder = resolvePath(cfg.driversFolder() == null ? "jdbc-drivers" : cfg.driversFolder());
+    }
+
+    /**
+     * @param jdbcDriverJarPath JDBC driver JAR path.
+     * @param jdbcDriverCls JDBC driver class.
+     * @param jdbcUrl JDBC URL.
+     * @param jdbcInfo Properties to connect to database.
+     * @return Connection to database.
+     * @throws SQLException
+     */
+    private Connection connect(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
+        Properties jdbcInfo) throws SQLException {
+        if (AgentMetadataDemo.isTestDriveUrl(jdbcUrl))
+            return AgentMetadataDemo.testDrive();
+
+        if (!new File(jdbcDriverJarPath).isAbsolute() && driversFolder != null)
+            jdbcDriverJarPath = new File(driversFolder, jdbcDriverJarPath).getPath();
+
+        return DbMetadataReader.getInstance().connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo);
+    }
+
+    /**
+     * @param jdbcDriverJarPath JDBC driver JAR path.
+     * @param jdbcDriverCls JDBC driver class.
+     * @param jdbcUrl JDBC URL.
+     * @param jdbcInfo Properties to connect to database.
+     * @return Collection of schema names.
+     * @throws SQLException
+     */
+    protected Collection<String> schemas(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
+        Properties jdbcInfo) throws SQLException {
+        if (log.isDebugEnabled())
+            log.debug("Start collecting database schemas [drvJar=" + jdbcDriverJarPath +
+                ", drvCls=" + jdbcDriverCls + ", jdbcUrl=" + jdbcUrl + "]");
+
+        try (Connection conn = connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo)) {
+            Collection<String> schemas = DbMetadataReader.getInstance().schemas(conn);
+
+            if (log.isDebugEnabled())
+                log.debug("Finished collection of schemas [jdbcUrl=" + jdbcUrl + ", count=" + schemas.size() + "]");
+
+            return schemas;
+        }
+        catch (Throwable e) {
+            log.error("Failed to collect schemas", e);
+
+            throw new SQLException("Failed to collect schemas", e);
+        }
+    }
+
+    /**
+     * Listener for drivers.
+     *
+     * @return Drivers in drivers folder
+     * @see AgentConfiguration#driversFolder
+     */
+    public Emitter.Listener availableDriversListener() {
+        return availableDriversLsnr;
+    }
+
+    /**
+     * Listener for schema names.
+     *
+     * @return Collection of schema names.
+     */
+    public Emitter.Listener schemasListener() {
+        return schemasLsnr;
+    }
+
+    /**
+     * @param jdbcDriverJarPath JDBC driver JAR path.
+     * @param jdbcDriverCls JDBC driver class.
+     * @param jdbcUrl JDBC URL.
+     * @param jdbcInfo Properties to connect to database.
+     * @param schemas List of schema names to process.
+     * @param tblsOnly If {@code true} then only tables will be processed otherwise views also will be processed.
+     * @return Collection of tables.
+     */
+    protected Collection<DbTable> metadata(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
+        Properties jdbcInfo, List<String> schemas, boolean tblsOnly) throws SQLException {
+        if (log.isDebugEnabled())
+            log.debug("Start collecting database metadata [drvJar=" + jdbcDriverJarPath +
+                ", drvCls=" + jdbcDriverCls + ", jdbcUrl=" + jdbcUrl + "]");
+
+        try (Connection conn = connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo)) {
+            Collection<DbTable> metadata = DbMetadataReader.getInstance().metadata(conn, schemas, tblsOnly);
+
+            if (log.isDebugEnabled())
+                log.debug("Finished collection of metadata [jdbcUrl=" + jdbcUrl + ", count=" + metadata.size() + "]");
+
+            return metadata;
+        }
+        catch (Throwable e) {
+            log.error("Failed to collect metadata", e);
+
+            throw new SQLException("Failed to collect metadata", e);
+        }
+    }
+
+    /**
+     * Listener for tables.
+     *
+     * @return Collection of tables.
+     */
+    public Emitter.Listener metadataListener() {
+        return metadataLsnr;
+    }
+
+    /**
+     * Stop handler.
+     */
+    public void stop() {
+        availableDriversLsnr.stop();
+
+        schemasLsnr.stop();
+
+        metadataLsnr.stop();
+    }
+
+    /**
+     * Wrapper class for later to be transformed to JSON and send to Web Console.
+     */
+    private static class JdbcDriver {
+        /** */
+        public final String jdbcDriverJar;
+        /** */
+        public final String jdbcDriverCls;
+
+        /**
+         * @param jdbcDriverJar File name of driver jar file.
+         * @param jdbcDriverCls Optional JDBC driver class.
+         */
+        public JdbcDriver(String jdbcDriverJar, String jdbcDriverCls) {
+            this.jdbcDriverJar = jdbcDriverJar;
+            this.jdbcDriverCls = jdbcDriverCls;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestHandler.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestHandler.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestHandler.java
deleted file mode 100644
index 1b4b565..0000000
--- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestHandler.java
+++ /dev/null
@@ -1,276 +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 java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.ConnectException;
-import java.net.URISyntaxException;
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.codec.Charsets;
-import org.apache.http.Header;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.ignite.console.agent.AgentConfiguration;
-import org.apache.ignite.console.demo.AgentClusterDemo;
-import org.apache.log4j.Logger;
-
-import static org.apache.ignite.console.agent.AgentConfiguration.DFLT_NODE_PORT;
-
-/**
- * API to translate REST requests to Ignite cluster.
- */
-public class RestHandler extends AbstractHandler {
-    /** */
-    private static final Logger log = Logger.getLogger(RestHandler.class.getName());
-
-    /** */
-    private final AgentConfiguration cfg;
-
-    /** */
-    private CloseableHttpClient httpClient;
-
-    /**
-     * @param cfg Config.
-     */
-    public RestHandler(AgentConfiguration cfg) {
-        this.cfg = cfg;
-    }
-
-    /**
-     * Start HTTP client for communication with node via REST.
-     */
-    public void start() {
-        httpClient = HttpClientBuilder.create().build();
-    }
-
-    /**
-     * Stop HTTP client.
-     */
-    public void stop() {
-        if (httpClient != null) {
-            try {
-                httpClient.close();
-            }
-            catch (IOException ignore) {
-                // No-op.
-            }
-        }
-    }
-
-    /** {@inheritDoc} */
-    @SuppressWarnings("unchecked")
-    @Override public Object execute(Map<String, Object> args) throws Exception {
-        if (log.isDebugEnabled())
-            log.debug("Start parse REST command args: " + args);
-
-        String uri = null;
-
-        if (args.containsKey("uri"))
-            uri = args.get("uri").toString();
-
-        Map<String, Object> params = null;
-
-        if (args.containsKey("params"))
-            params = (Map<String, Object>)args.get("params");
-
-        if (!args.containsKey("demo"))
-            throw new IllegalArgumentException("Missing demo flag in arguments: " + args);
-
-        boolean demo = (boolean)args.get("demo");
-
-        if (!args.containsKey("method"))
-            throw new IllegalArgumentException("Missing method in arguments: " + args);
-
-        String mtd = args.get("method").toString();
-
-        Map<String, Object> headers = null;
-
-        if (args.containsKey("headers"))
-            headers = (Map<String, Object>)args.get("headers");
-
-        String body = null;
-
-        if (args.containsKey("body"))
-            body = args.get("body").toString();
-
-        return executeRest(uri, params, demo, mtd, headers, body);
-    }
-
-    /**
-     * @param uri Url.
-     * @param params Params.
-     * @param demo Use demo node.
-     * @param mtd Method.
-     * @param headers Headers.
-     * @param body Body.
-     */
-    protected RestResult executeRest(String uri, Map<String, Object> params, boolean demo,
-        String mtd, Map<String, Object> headers, String body) throws IOException, URISyntaxException {
-        if (log.isDebugEnabled())
-            log.debug("Start execute REST command [method=" + mtd + ", uri=/" + (uri == null ? "" : uri) +
-                ", parameters=" + params + "]");
-
-        final URIBuilder builder;
-
-        if (demo) {
-            // try start demo if needed.
-            AgentClusterDemo.testDrive(cfg);
-
-            // null if demo node not started yet.
-            if (cfg.demoNodeUri() == null)
-                return RestResult.fail("Demo node is not started yet.", 404);
-
-            builder = new URIBuilder(cfg.demoNodeUri());
-        }
-        else
-            builder = new URIBuilder(cfg.nodeUri());
-
-        if (builder.getPort() == -1)
-            builder.setPort(DFLT_NODE_PORT);
-
-        if (uri != null) {
-            if (!uri.startsWith("/") && !cfg.nodeUri().endsWith("/"))
-                uri = '/' + uri;
-
-            builder.setPath(uri);
-        }
-
-        if (params != null) {
-            for (Map.Entry<String, Object> entry : params.entrySet()) {
-                if (entry.getValue() != null)
-                    builder.addParameter(entry.getKey(), entry.getValue().toString());
-            }
-        }
-
-        HttpRequestBase httpReq = null;
-
-        try {
-            if ("GET".equalsIgnoreCase(mtd))
-                httpReq = new HttpGet(builder.build());
-            else if ("POST".equalsIgnoreCase(mtd)) {
-                HttpPost post;
-
-                if (body == null) {
-                    List<NameValuePair> nvps = builder.getQueryParams();
-
-                    builder.clearParameters();
-
-                    post = new HttpPost(builder.build());
-
-                    if (!nvps.isEmpty())
-                        post.setEntity(new UrlEncodedFormEntity(nvps));
-                }
-                else {
-                    post = new HttpPost(builder.build());
-
-                    post.setEntity(new StringEntity(body));
-                }
-
-                httpReq = post;
-            }
-            else
-                throw new IOException("Unknown HTTP-method: " + mtd);
-
-            if (headers != null) {
-                for (Map.Entry<String, Object> entry : headers.entrySet())
-                    httpReq.addHeader(entry.getKey(), entry.getValue() == null ? null : entry.getValue().toString());
-            }
-
-            try (CloseableHttpResponse resp = httpClient.execute(httpReq)) {
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-                resp.getEntity().writeTo(out);
-
-                Charset charset = Charsets.UTF_8;
-
-                Header encodingHdr = resp.getEntity().getContentEncoding();
-
-                if (encodingHdr != null) {
-                    String encoding = encodingHdr.getValue();
-
-                    charset = Charsets.toCharset(encoding);
-                }
-
-                return RestResult.success(resp.getStatusLine().getStatusCode(), new String(out.toByteArray(), charset));
-            }
-            catch (ConnectException e) {
-                log.info("Failed connect to node and execute REST command [uri=" + builder.build() + "]");
-
-                return RestResult.fail("Failed connect to node and execute REST command.", 404);
-            }
-        }
-        finally {
-            if (httpReq != null)
-                httpReq.reset();
-        }
-    }
-
-    /**
-     * Request result.
-     */
-    public static class RestResult {
-        /** The field contains description of error if server could not handle the request. */
-        public final String error;
-
-        /** REST http code. */
-        public final int code;
-
-        /** The field contains result of command. */
-        public final String data;
-
-        /**
-         * @param error The field contains description of error if server could not handle the request.
-         * @param resCode REST http code.
-         * @param res The field contains result of command.
-         */
-        private RestResult(String error, int resCode, String res) {
-            this.error = error;
-            this.code = resCode;
-            this.data = res;
-        }
-
-        /**
-         * @param error The field contains description of error if server could not handle the request.
-         * @param restCode REST http code.
-         * @return Request result.
-         */
-        public static RestResult fail(String error, int restCode) {
-            return new RestResult(error, restCode, null);
-        }
-
-        /**
-         * @param code REST http code.
-         * @param data The field contains result of command.
-         * @return Request result.
-         */
-        public static RestResult success(int code, String data) {
-            return new RestResult(null, code, data);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/262a3410/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
new file mode 100644
index 0000000..1e86549
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/RestListener.java
@@ -0,0 +1,280 @@
+/*
+ * 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 java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.commons.codec.Charsets;
+import org.apache.http.Header;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.ignite.console.agent.AgentConfiguration;
+import org.apache.ignite.console.demo.AgentClusterDemo;
+import org.apache.log4j.Logger;
+
+import static org.apache.ignite.console.agent.AgentConfiguration.DFLT_NODE_PORT;
+
+/**
+ * API to translate REST requests to Ignite cluster.
+ */
+public class RestListener extends AbstractListener {
+    /** */
+    private static final Logger log = Logger.getLogger(RestListener.class.getName());
+
+    /** */
+    private final AgentConfiguration cfg;
+
+    /** */
+    private CloseableHttpClient httpClient;
+
+    /**
+     * @param cfg Config.
+     */
+    public RestListener(AgentConfiguration cfg) {
+        super();
+
+        this.cfg = cfg;
+
+        httpClient = HttpClientBuilder.create().build();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() {
+        super.stop();
+
+        if (httpClient != null) {
+            try {
+                httpClient.close();
+            }
+            catch (IOException ignore) {
+                // No-op.
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected ExecutorService newThreadPool() {
+        return Executors.newCachedThreadPool();
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public Object execute(Map<String, Object> args) throws Exception {
+        if (log.isDebugEnabled())
+            log.debug("Start parse REST command args: " + args);
+
+        String uri = null;
+
+        if (args.containsKey("uri"))
+            uri = args.get("uri").toString();
+
+        Map<String, Object> params = null;
+
+        if (args.containsKey("params"))
+            params = (Map<String, Object>)args.get("params");
+
+        if (!args.containsKey("demo"))
+            throw new IllegalArgumentException("Missing demo flag in arguments: " + args);
+
+        boolean demo = (boolean)args.get("demo");
+
+        if (!args.containsKey("method"))
+            throw new IllegalArgumentException("Missing method in arguments: " + args);
+
+        String mtd = args.get("method").toString();
+
+        Map<String, Object> headers = null;
+
+        if (args.containsKey("headers"))
+            headers = (Map<String, Object>)args.get("headers");
+
+        String body = null;
+
+        if (args.containsKey("body"))
+            body = args.get("body").toString();
+
+        return executeRest(uri, params, demo, mtd, headers, body);
+    }
+
+    /**
+     * @param uri Url.
+     * @param params Params.
+     * @param demo Use demo node.
+     * @param mtd Method.
+     * @param headers Headers.
+     * @param body Body.
+     */
+    protected RestResult executeRest(String uri, Map<String, Object> params, boolean demo,
+        String mtd, Map<String, Object> headers, String body) throws IOException, URISyntaxException {
+        if (log.isDebugEnabled())
+            log.debug("Start execute REST command [method=" + mtd + ", uri=/" + (uri == null ? "" : uri) +
+                ", parameters=" + params + "]");
+
+        final URIBuilder builder;
+
+        if (demo) {
+            // try start demo if needed.
+            AgentClusterDemo.testDrive(cfg);
+
+            // null if demo node not started yet.
+            if (cfg.demoNodeUri() == null)
+                return RestResult.fail("Demo node is not started yet.", 404);
+
+            builder = new URIBuilder(cfg.demoNodeUri());
+        }
+        else
+            builder = new URIBuilder(cfg.nodeUri());
+
+        if (builder.getPort() == -1)
+            builder.setPort(DFLT_NODE_PORT);
+
+        if (uri != null) {
+            if (!uri.startsWith("/") && !cfg.nodeUri().endsWith("/"))
+                uri = '/' + uri;
+
+            builder.setPath(uri);
+        }
+
+        if (params != null) {
+            for (Map.Entry<String, Object> entry : params.entrySet()) {
+                if (entry.getValue() != null)
+                    builder.addParameter(entry.getKey(), entry.getValue().toString());
+            }
+        }
+
+        HttpRequestBase httpReq = null;
+
+        try {
+            if ("GET".equalsIgnoreCase(mtd))
+                httpReq = new HttpGet(builder.build());
+            else if ("POST".equalsIgnoreCase(mtd)) {
+                HttpPost post;
+
+                if (body == null) {
+                    List<NameValuePair> nvps = builder.getQueryParams();
+
+                    builder.clearParameters();
+
+                    post = new HttpPost(builder.build());
+
+                    if (!nvps.isEmpty())
+                        post.setEntity(new UrlEncodedFormEntity(nvps));
+                }
+                else {
+                    post = new HttpPost(builder.build());
+
+                    post.setEntity(new StringEntity(body));
+                }
+
+                httpReq = post;
+            }
+            else
+                throw new IOException("Unknown HTTP-method: " + mtd);
+
+            if (headers != null) {
+                for (Map.Entry<String, Object> entry : headers.entrySet())
+                    httpReq.addHeader(entry.getKey(), entry.getValue() == null ? null : entry.getValue().toString());
+            }
+
+            try (CloseableHttpResponse resp = httpClient.execute(httpReq)) {
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+                resp.getEntity().writeTo(out);
+
+                Charset charset = Charsets.UTF_8;
+
+                Header encodingHdr = resp.getEntity().getContentEncoding();
+
+                if (encodingHdr != null) {
+                    String encoding = encodingHdr.getValue();
+
+                    charset = Charsets.toCharset(encoding);
+                }
+
+                return RestResult.success(resp.getStatusLine().getStatusCode(), new String(out.toByteArray(), charset));
+            }
+            catch (ConnectException e) {
+                log.info("Failed connect to node and execute REST command [uri=" + builder.build() + "]");
+
+                return RestResult.fail("Failed connect to node and execute REST command.", 404);
+            }
+        }
+        finally {
+            if (httpReq != null)
+                httpReq.reset();
+        }
+    }
+
+    /**
+     * Request result.
+     */
+    public static class RestResult {
+        /** The field contains description of error if server could not handle the request. */
+        public final String error;
+
+        /** REST http code. */
+        public final int code;
+
+        /** The field contains result of command. */
+        public final String data;
+
+        /**
+         * @param error The field contains description of error if server could not handle the request.
+         * @param resCode REST http code.
+         * @param res The field contains result of command.
+         */
+        private RestResult(String error, int resCode, String res) {
+            this.error = error;
+            this.code = resCode;
+            this.data = res;
+        }
+
+        /**
+         * @param error The field contains description of error if server could not handle the request.
+         * @param restCode REST http code.
+         * @return Request result.
+         */
+        public static RestResult fail(String error, int restCode) {
+            return new RestResult(error, restCode, null);
+        }
+
+        /**
+         * @param code REST http code.
+         * @param data The field contains result of command.
+         * @return Request result.
+         */
+        public static RestResult success(int code, String data) {
+            return new RestResult(null, code, data);
+        }
+    }
+}