ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dma...@apache.org
Subject [08/21] ignite git commit: IGNITE-3143 Implemented support for executing Visor tasks via REST HTTP.
Date Mon, 16 May 2016 14:43:04 GMT
IGNITE-3143 Implemented support for executing Visor tasks via REST HTTP.


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

Branch: refs/heads/ignite-3098
Commit: 0c3f5d9b72ce7c911bb9e71ae99b043924318002
Parents: dcfd30e
Author: AKuznetsov <akuznetsov@gridgain.com>
Authored: Mon May 16 18:45:33 2016 +0700
Committer: AKuznetsov <akuznetsov@gridgain.com>
Committed: Mon May 16 18:45:33 2016 +0700

----------------------------------------------------------------------
 .../JettyRestProcessorAbstractSelfTest.java     | 716 ++++++++++++++++++-
 .../rest/JettyRestProcessorSignedSelfTest.java  |   5 +-
 .../JettyRestProcessorUnsignedSelfTest.java     |   5 +-
 .../visor/compute/VisorGatewayTask.java         | 361 ++++++++++
 .../internal/visor/util/VisorTaskUtils.java     |  10 +-
 .../http/jetty/GridJettyJsonConfig.java         | 288 +++++---
 .../http/jetty/GridJettyRestHandler.java        |   2 +-
 .../http/jetty/GridJettyRestProtocol.java       |   2 +-
 8 files changed, 1291 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
index 4b1d47c..9fd3044 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
@@ -29,27 +29,77 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 import net.sf.json.JSONNull;
 import net.sf.json.JSONObject;
 import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
 import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.FileSystemConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.igfs.IgfsGroupDataBlocksKeyMapper;
+import org.apache.ignite.igfs.IgfsIpcEndpointConfiguration;
 import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlIndexMetadata;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata;
 import org.apache.ignite.internal.processors.rest.handlers.GridRestCommandHandler;
+import org.apache.ignite.internal.util.lang.GridTuple3;
+import org.apache.ignite.internal.util.typedef.C1;
 import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.util.typedef.P1;
+import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.visor.cache.VisorCacheClearTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheConfigurationCollectorTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheLoadTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheMetadataTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheMetricsCollectorTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheNodesTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheRebalanceTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheResetMetricsTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheStartTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheStopTask;
+import org.apache.ignite.internal.visor.cache.VisorCacheSwapBackupsTask;
+import org.apache.ignite.internal.visor.compute.VisorComputeCancelSessionsTask;
+import org.apache.ignite.internal.visor.compute.VisorComputeResetMetricsTask;
+import org.apache.ignite.internal.visor.compute.VisorComputeToggleMonitoringTask;
+import org.apache.ignite.internal.visor.compute.VisorGatewayTask;
+import org.apache.ignite.internal.visor.debug.VisorThreadDumpTask;
+import org.apache.ignite.internal.visor.file.VisorFileBlockTask;
+import org.apache.ignite.internal.visor.file.VisorLatestTextFilesTask;
+import org.apache.ignite.internal.visor.igfs.VisorIgfsFormatTask;
+import org.apache.ignite.internal.visor.igfs.VisorIgfsProfilerClearTask;
+import org.apache.ignite.internal.visor.igfs.VisorIgfsProfilerTask;
+import org.apache.ignite.internal.visor.igfs.VisorIgfsResetMetricsTask;
+import org.apache.ignite.internal.visor.igfs.VisorIgfsSamplingStateTask;
+import org.apache.ignite.internal.visor.log.VisorLogSearchTask;
+import org.apache.ignite.internal.visor.misc.VisorAckTask;
+import org.apache.ignite.internal.visor.misc.VisorLatestVersionTask;
+import org.apache.ignite.internal.visor.misc.VisorResolveHostNameTask;
+import org.apache.ignite.internal.visor.node.VisorNodeConfigurationCollectorTask;
+import org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTask;
+import org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTaskArg;
+import org.apache.ignite.internal.visor.node.VisorNodeEventsCollectorTask;
+import org.apache.ignite.internal.visor.node.VisorNodeGcTask;
+import org.apache.ignite.internal.visor.node.VisorNodePingTask;
+import org.apache.ignite.internal.visor.node.VisorNodeSuppressedErrorsTask;
+import org.apache.ignite.internal.visor.query.VisorQueryArg;
+import org.apache.ignite.internal.visor.query.VisorQueryCleanupTask;
+import org.apache.ignite.internal.visor.query.VisorQueryNextPageTask;
+import org.apache.ignite.internal.visor.query.VisorQueryTask;
 import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.testframework.GridTestUtils;
 
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_JETTY_PORT;
@@ -138,7 +188,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
      * @param ptrn Pattern to match.
      */
     @SuppressWarnings("TypeMayBeWeakened")
-    private void jsonEquals(String json, String ptrn) {
+    protected void jsonEquals(String json, String ptrn) {
         assertTrue("JSON mismatch [json=" + json + ", ptrn=" + ptrn + ']', Pattern.matches(ptrn, json));
     }
 
@@ -249,7 +299,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
      * @param success Success flag.
      * @return Regex pattern for JSON.
      */
-    private String pattern(String res, boolean success) {
+    protected String pattern(String res, boolean success) {
         return "\\{\\\"error\\\":\\\"" + (!success ? ".+" : "") + "\\\"\\," +
             "\\\"response\\\":" + res + "\\," +
             "\\\"sessionToken\\\":\\\"" + (securityEnabled() && success ? ".+" : "") + "\\\"," +
@@ -1172,6 +1222,414 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
     }
 
     /**
+     * Tests execution of Visor tasks via {@link VisorGatewayTask}.
+     *
+     * @throws Exception If failed.
+     */
+    public void testVisorGateway() throws Exception {
+        ClusterNode locNode = grid(1).localNode();
+
+        final String successRes = pattern(
+            "\\{\\\"error\\\":\\\"\\\",\\\"finished\\\":true,\\\"id\\\":\\\"[^\\\"]+\\\",\\\"result\\\":.+}", true);
+
+        final IgniteUuid cid = grid(1).context().cache().internalCache("person").context().dynamicDeploymentId();
+
+        String ret = content(new VisorGatewayArgument(VisorCacheConfigurationCollectorTask.class)
+            .forNode(locNode)
+            .collection(IgniteUuid.class, cid));
+
+        info("VisorCacheConfigurationCollectorTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheNodesTask.class)
+            .forNode(locNode)
+            .argument("person"));
+
+        info("VisorCacheNodesTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheLoadTask.class)
+            .forNode(locNode)
+            .tuple3(Set.class, Long.class, Object[].class, "person", 0, "null"));
+
+        info("VisorCacheLoadTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheSwapBackupsTask.class)
+            .forNode(locNode)
+            .set(String.class, "person"));
+
+        info("VisorCacheSwapBackupsTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheRebalanceTask.class)
+            .forNode(locNode)
+            .set(String.class, "person"));
+
+        info("VisorCacheRebalanceTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheMetadataTask.class)
+            .forNode(locNode)
+            .argument("person"));
+
+        info("VisorCacheMetadataTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheResetMetricsTask.class)
+            .forNode(locNode)
+            .argument("person"));
+
+        info("VisorCacheResetMetricsTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorIgfsSamplingStateTask.class)
+            .forNode(locNode)
+            .pair(String.class, Boolean.class, "igfs", false));
+
+        info("VisorIgfsSamplingStateTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorIgfsProfilerClearTask.class)
+            .forNode(locNode)
+            .argument("igfs"));
+
+        info("VisorIgfsProfilerClearTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorIgfsProfilerTask.class)
+            .forNode(locNode)
+            .argument("igfs"));
+
+        info("VisorIgfsProfilerTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorIgfsFormatTask.class)
+            .forNode(locNode)
+            .argument("igfs"));
+
+        info("VisorIgfsFormatTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorIgfsResetMetricsTask.class)
+            .forNode(locNode)
+            .set(String.class, "igfs"));
+
+        info("VisorIgfsResetMetricsTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorThreadDumpTask.class)
+            .forNode(locNode));
+
+        info("VisorThreadDumpTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorLatestTextFilesTask.class)
+            .forNode(locNode)
+            .pair(String.class, String.class, "", ""));
+
+        info("VisorLatestTextFilesTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorLatestVersionTask.class)
+            .forNode(locNode));
+
+        info("VisorLatestVersionTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorFileBlockTask.class)
+            .forNode(locNode)
+            .argument(VisorFileBlockTask.VisorFileBlockArg.class, "", 0L, 1, 0L));
+
+        info("VisorFileBlockTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorNodePingTask.class)
+            .forNode(locNode)
+            .argument(UUID.class, locNode.id()));
+
+        info("VisorNodePingTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorNodeConfigurationCollectorTask.class)
+            .forNode(locNode));
+
+        info("VisorNodeConfigurationCollectorTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorComputeResetMetricsTask.class)
+            .forNode(locNode));
+
+        info("VisorComputeResetMetricsTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorQueryTask.class)
+            .forNode(locNode)
+            .argument(VisorQueryArg.class, "person", URLEncoder.encode("select * from Person"), false, 1));
+
+        info("VisorQueryTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        JSONObject json = JSONObject.fromObject(ret);
+
+        final String qryId = (String)((Map)((Map)((Map)json.get("response")).get("result")).get("value")).get("queryId");
+
+        ret = content(new VisorGatewayArgument(VisorQueryNextPageTask.class)
+            .forNode(locNode)
+            .pair(String.class, Integer.class, qryId, 1));
+
+        info("VisorQueryNextPageTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorQueryCleanupTask.class)
+            .map(UUID.class, Set.class, F.asMap(locNode.id(), qryId)));
+
+        info("VisorQueryCleanupTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorResolveHostNameTask.class)
+            .forNode(locNode));
+
+        info("VisorResolveHostNameTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        // Multinode tasks
+
+        ret = content(new VisorGatewayArgument(VisorComputeCancelSessionsTask.class)
+            .map(UUID.class, Set.class, new HashMap()));
+
+        info("VisorComputeCancelSessionsTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheMetricsCollectorTask.class)
+            .pair(Boolean.class, Set.class, false, "person"));
+
+        info("VisorCacheMetricsCollectorTask result: " + ret);
+
+        ret = content(new VisorGatewayArgument(VisorCacheMetricsCollectorTask.class)
+            .forNodes(grid(1).cluster().nodes())
+            .pair(Boolean.class, Set.class, false, "person"));
+
+        info("VisorCacheMetricsCollectorTask (with nodes) result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorLogSearchTask.class)
+            .argument(VisorLogSearchTask.VisorLogSearchArg.class, ".", ".", "abrakodabra.txt", 1));
+
+        info("VisorLogSearchTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorNodeGcTask.class));
+
+        info("VisorNodeGcTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorAckTask.class)
+            .argument("MSG"));
+
+        info("VisorAckTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorNodeEventsCollectorTask.class)
+            .argument(VisorNodeEventsCollectorTask.VisorNodeEventsCollectorTaskArg.class,
+                "null", "null", "null", "taskName", "null"));
+
+        info("VisorNodeEventsCollectorTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorNodeDataCollectorTask.class)
+            .argument(VisorNodeDataCollectorTaskArg.class, false,
+                "CONSOLE_" + UUID.randomUUID(), UUID.randomUUID(), 10, false));
+
+        info("VisorNodeDataCollectorTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorComputeToggleMonitoringTask.class)
+            .pair(String.class, Boolean.class, UUID.randomUUID(), false));
+
+        info("VisorComputeToggleMonitoringTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorNodeSuppressedErrorsTask.class)
+            .map(UUID.class, Long.class, new HashMap()));
+
+        info("VisorNodeSuppressedErrorsTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheClearTask.class)
+            .forNode(locNode)
+            .argument("person"));
+
+        info("VisorCacheClearTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        /** Spring XML to start cache via Visor task. */
+        final String START_CACHE =
+            "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n" +
+                    "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+                    "    xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n" +
+                    "        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\">\n" +
+                    "    <bean id=\"cacheConfiguration\" class=\"org.apache.ignite.configuration.CacheConfiguration\">\n" +
+                    "        <property name=\"cacheMode\" value=\"PARTITIONED\"/>\n" +
+                    "        <property name=\"name\" value=\"c\"/>\n" +
+                    "   </bean>\n" +
+                    "</beans>";
+
+        ret = content(new VisorGatewayArgument(VisorCacheStartTask.class)
+            .argument(VisorCacheStartTask.VisorCacheStartArg.class, false, "person2", URLEncoder.encode(START_CACHE)));
+
+        info("VisorCacheStartTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+
+        ret = content(new VisorGatewayArgument(VisorCacheStopTask.class)
+            .forNode(locNode)
+            .argument(String.class, "c"));
+
+        info("VisorCacheStopTask result: " + ret);
+
+        assertNotNull(ret);
+        assertTrue(!ret.isEmpty());
+
+        jsonEquals(ret, successRes);
+    }
+
+    /**
      * @throws Exception If failed.
      */
     public void testVersion() throws Exception {
@@ -1473,6 +1931,10 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
         assertFalse(queryCursorFound());
     }
 
+    /**
+     * @return Signature.
+     * @throws Exception If failed.
+     */
     protected abstract String signature() throws Exception;
 
     /**
@@ -1500,6 +1962,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
      */
     private void initCache() {
         CacheConfiguration<Integer, Person> personCacheCfg = new CacheConfiguration<>("person");
+
         personCacheCfg.setIndexedTypes(Integer.class, Person.class);
 
         IgniteCache<Integer, Person> personCache = grid(0).getOrCreateCache(personCacheCfg);
@@ -1615,4 +2078,251 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro
             return n.id().equals(nid);
         }
     }
+
+    /**
+     * Helper for build {@link VisorGatewayTask} arguments.
+     */
+    public static class VisorGatewayArgument extends HashMap<String, String> {
+        /** Latest argument index. */
+        private int idx = 3;
+
+        /**
+         * Construct helper object.
+         *
+         * @param cls Class of executed task.
+         */
+        public VisorGatewayArgument(Class cls) {
+            super(F.asMap(
+                "cmd", GridRestCommand.EXE.key(),
+                "name", VisorGatewayTask.class.getName(),
+                "p1", "null",
+                "p2", cls.getName()
+            ));
+        }
+
+        /**
+         * Execute task on node.
+         *
+         * @param node Node.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument forNode(ClusterNode node) {
+            put("p1", node.id().toString());
+
+            return this;
+        }
+
+        /**
+         * Prepare list of node IDs.
+         *
+         * @param nodes Collection of nodes.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument forNodes(Collection<ClusterNode> nodes) {
+            put("p1", concat(F.transform(nodes, new C1<ClusterNode, UUID>() {
+                /** {@inheritDoc} */
+                @Override public UUID apply(ClusterNode node) {
+                    return node.id();
+                }
+            }).toArray(), ";"));
+
+            return this;
+        }
+
+        /**
+         * Add string argument.
+         *
+         * @param val Value.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument argument(String val) {
+            put("p" + idx++, String.class.getName());
+            put("p" + idx++, val);
+
+            return this;
+        }
+
+        /**
+         * Add custom class argument.
+         *
+         * @param cls Class.
+         * @param vals Values.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument argument(Class cls, Object ... vals) {
+            put("p" + idx++, cls.getName());
+
+            for (Object val : vals)
+                put("p" + idx++, val != null ? val.toString() : null);
+
+            return this;
+        }
+
+        /**
+         * Add collection argument.
+         *
+         * @param cls Class.
+         * @param vals Values.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument collection(Class cls, Object ... vals) {
+            put("p" + idx++, Collection.class.getName());
+            put("p" + idx++, cls.getName());
+            put("p" + idx++, concat(vals, ";"));
+
+            return this;
+        }
+
+        /**
+         * Add tuple argument.
+         *
+         * @param keyCls Key class.
+         * @param valCls Values class.
+         * @param key Key.
+         * @param val Value.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument pair(Class keyCls, Class valCls, Object key, Object val) {
+            put("p" + idx++, IgniteBiTuple.class.getName());
+            put("p" + idx++, keyCls.getName());
+            put("p" + idx++, valCls.getName());
+            put("p" + idx++, key != null ? key.toString() : "null");
+            put("p" + idx++, val != null ? val.toString() : "null");
+
+            return this;
+        }
+
+        /**
+         * Add tuple argument.
+         *
+         * @param firstCls Class of first argument.
+         * @param secondCls Class of second argument.
+         * @param thirdCls Class of third argument.
+         * @param first First argument.
+         * @param second Second argument.
+         * @param third Third argument.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument tuple3(Class firstCls, Class secondCls, Class thirdCls,
+            Object first, Object second, Object third) {
+            put("p" + idx++, GridTuple3.class.getName());
+            put("p" + idx++, firstCls.getName());
+            put("p" + idx++, secondCls.getName());
+            put("p" + idx++, thirdCls.getName());
+            put("p" + idx++, first != null ? first.toString() : "null");
+            put("p" + idx++, second != null ? second.toString() : "null");
+            put("p" + idx++, third != null ? third.toString() : "null");
+
+            return this;
+        }
+
+        /**
+         * Add set argument.
+         *
+         * @param cls Class.
+         * @param vals Values.
+         * @return This helper for chaining method calls.
+         */
+        public VisorGatewayArgument set(Class cls, Object ... vals) {
+            put("p" + idx++, Set.class.getName());
+            put("p" + idx++, cls.getName());
+            put("p" + idx++, concat(vals, ";"));
+
+            return this;
+        }
+
+        /**
+         * Add map argument.
+         *
+         * @param keyCls Key class.
+         * @param valCls Value class.
+         * @param map Map.
+         */
+        public VisorGatewayArgument map(Class keyCls, Class valCls, Map<?, ?> map) {
+            put("p" + idx++, Map.class.getName());
+            put("p" + idx++, keyCls.getName());
+            put("p" + idx++, valCls.getName());
+
+            SB sb = new SB();
+
+            boolean first = true;
+
+            for (Map.Entry<?, ?> entry : map.entrySet()) {
+                if (!first)
+                    sb.a(";");
+
+                sb.a(entry.getKey());
+
+                if (entry.getValue() != null)
+                    sb.a("=").a(entry.getValue());
+
+                first = false;
+            }
+
+            put("p" + idx++, URLEncoder.encode(sb.toString()));
+
+            return this;
+        }
+
+        /**
+         * Concat object with delimiter.
+         *
+         * @param vals Values.
+         * @param delim Delimiter.
+         */
+        private static String concat(Object[] vals, String delim) {
+            SB sb = new SB();
+
+            boolean first = true;
+
+            for (Object val : vals) {
+                if (!first)
+                    sb.a(delim);
+
+                sb.a(val);
+
+                first = false;
+            }
+
+            return sb.toString();
+        };
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        CacheConfiguration cacheIgfs_data = new CacheConfiguration();
+
+        cacheIgfs_data.setName("igfs-data");
+        cacheIgfs_data.setCacheMode(CacheMode.PARTITIONED);
+        cacheIgfs_data.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
+        cacheIgfs_data.setBackups(0);
+
+        cacheIgfs_data.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
+
+        cacheIgfs_data.setAffinityMapper(new IgfsGroupDataBlocksKeyMapper(512));
+
+        CacheConfiguration cacheIgfs_meta = new CacheConfiguration();
+
+        cacheIgfs_meta.setName("igfs-meta");
+        cacheIgfs_meta.setCacheMode(CacheMode.REPLICATED);
+        cacheIgfs_meta.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
+
+        cacheIgfs_meta.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
+
+        cfg.setCacheConfiguration(cfg.getCacheConfiguration()[0], cacheIgfs_data, cacheIgfs_meta);
+
+        FileSystemConfiguration igfs = new FileSystemConfiguration();
+
+        igfs.setName("igfs");
+        igfs.setDataCacheName("igfs-data");
+        igfs.setMetaCacheName("igfs-meta");
+
+        igfs.setIpcEndpointConfiguration(new IgfsIpcEndpointConfiguration());
+
+        cfg.setFileSystemConfiguration(igfs);
+
+        return cfg;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorSignedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorSignedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorSignedSelfTest.java
index cf9fa9e..ad00f52 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorSignedSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorSignedSelfTest.java
@@ -78,10 +78,7 @@ public class JettyRestProcessorSignedSelfTest extends JettyRestProcessorAbstract
         assertEquals(200, ((HttpURLConnection)conn).getResponseCode());
     }
 
-    /**
-     * @return Signature.
-     * @throws Exception If failed.
-     */
+    /** {@inheritDoc} */
     @Override protected String signature() throws Exception {
         long ts = U.currentTimeMillis();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorUnsignedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorUnsignedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorUnsignedSelfTest.java
index e510ae8..988cedf 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorUnsignedSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorUnsignedSelfTest.java
@@ -26,10 +26,7 @@ public class JettyRestProcessorUnsignedSelfTest extends JettyRestProcessorAbstra
         return 8091;
     }
 
-    /**
-     * @return Signature.
-     * @throws Exception If failed.
-     */
+    /** {@inheritDoc} */
     @Override protected String signature() throws Exception {
         return null;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java
new file mode 100644
index 0000000..c59d299
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.compute;
+
+import java.lang.reflect.Constructor;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.compute.ComputeJobResultPolicy;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.util.lang.GridTuple3;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.visor.VisorTaskArgument;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Task to run Visor tasks through http REST.
+ */
+@GridInternal
+public class VisorGatewayTask implements ComputeTask<Object[], Object> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Auto-injected grid instance. */
+    @IgniteInstanceResource
+    protected transient IgniteEx ignite;
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid,
+        @Nullable Object[] args) throws IgniteException {
+        assert args != null;
+        assert args.length >= 2;
+
+        return Collections.singletonMap(new VisorGatewayJob(args), ignite.localNode());
+    }
+
+    /** {@inheritDoc} */
+    @Override public ComputeJobResultPolicy result(ComputeJobResult res,
+        List<ComputeJobResult> rcvd) throws IgniteException {
+        // Task should handle exceptions in reduce method.
+        return ComputeJobResultPolicy.WAIT;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object reduce(List<ComputeJobResult> results) throws IgniteException {
+        assert results.size() == 1;
+
+        ComputeJobResult res = F.first(results);
+
+        assert res != null;
+
+        IgniteException ex = res.getException();
+
+        if (ex != null)
+            throw ex;
+
+        return res.getData();
+    }
+
+    /**
+     * Job to run Visor tasks through http REST.
+     */
+    private static class VisorGatewayJob extends ComputeJobAdapter {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** Auto-injected grid instance. */
+        @IgniteInstanceResource
+        protected transient IgniteEx ignite;
+
+        /** Arguments count. */
+        private final int argsCnt;
+
+        /**
+         * Create job with specified argument.
+         *
+         * @param args Job argument.
+         */
+        VisorGatewayJob(@Nullable Object[] args) {
+            super(args);
+
+            assert args != null;
+
+            argsCnt = args.length;
+        }
+
+        /**
+         * Cast argument to target class.
+         *
+         * @param cls Class.
+         * @param idx Argument index.
+         */
+        @Nullable private Object toObject(Class cls, int idx) throws ClassNotFoundException {
+            String arg = argument(idx);
+
+            if (cls == Collection.class || cls == Set.class) {
+                Class<?> itemsCls = Class.forName(arg);
+
+                Collection<Object> res = cls == Collection.class ? new ArrayList<>() : new HashSet<>();
+
+                String items = argument(idx + 1);
+
+                if (items != null) {
+                    for (String item : items.split(";"))
+                        res.add(toSimpleObject(itemsCls, item));
+                }
+
+                return res;
+            }
+
+            if (cls == IgniteBiTuple.class) {
+                Class<?> keyCls = Class.forName(arg);
+
+                String valClsName = argument(idx + 1);
+
+                assert valClsName != null;
+
+                Class<?> valCls = Class.forName(valClsName);
+
+                return new IgniteBiTuple<>(toSimpleObject(keyCls, (String)argument(idx + 2)),
+                    toSimpleObject(valCls, (String)argument(idx + 3)));
+            }
+
+            if (cls == Map.class) {
+                Class<?> keyCls = Class.forName(arg);
+
+                String valClsName = argument(idx + 1);
+
+                assert valClsName != null;
+
+                Class<?> valCls = Class.forName(valClsName);
+
+                Map<Object, Object> res = new HashMap<>();
+
+                String entries = argument(idx + 2);
+
+                if (entries != null) {
+                    for (String entry : entries.split(";")) {
+                        if (entry.length() > 0) {
+                            String[] values = entry.split("=");
+
+                            assert values.length >= 1;
+
+                            res.put(toSimpleObject(keyCls, values[0]),
+                                    values.length > 1 ? toSimpleObject(valCls, values[1]) : null);
+                        }
+                    }
+                }
+
+                return res;
+            }
+
+            if (cls == GridTuple3.class) {
+                String v2ClsName = argument(idx + 1);
+                String v3ClsName = argument(idx + 2);
+
+                assert v2ClsName != null;
+                assert v3ClsName != null;
+
+                Class<?> v1Cls = Class.forName(arg);
+                Class<?> v2Cls = Class.forName(v2ClsName);
+                Class<?> v3Cls = Class.forName(v3ClsName);
+
+                return new GridTuple3<>(toSimpleObject(v1Cls, (String)argument(idx + 3)), toSimpleObject(v2Cls,
+                    (String)argument(idx + 4)), toSimpleObject(v3Cls, (String)argument(idx + 5)));
+            }
+
+            return toSimpleObject(cls, arg);
+        }
+
+        /**
+         * Cast from string representation to target class.
+         *
+         * @param cls Target class.
+         * @return Object constructed from string.
+         */
+        @Nullable private Object toSimpleObject(Class cls, String val) {
+            if (val == null  || val.equals("null"))
+                return null;
+
+            if (String.class == cls)
+                return val;
+
+            if (Boolean.class == cls || Boolean.TYPE == cls)
+                return Boolean.parseBoolean(val);
+
+            if (Integer.class == cls || Integer.TYPE == cls)
+                return Integer.parseInt(val);
+
+            if (Long.class == cls || Long.TYPE == cls)
+                return Long.parseLong(val);
+
+            if (UUID.class == cls)
+                return UUID.fromString(val);
+
+            if (IgniteUuid.class == cls)
+                return IgniteUuid.fromString(val);
+
+            if (Byte.class == cls || Byte.TYPE == cls)
+                return Byte.parseByte(val);
+
+            if (Short.class == cls || Short.TYPE == cls)
+                return Short.parseShort(val);
+
+            if (Float.class == cls || Float.TYPE == cls)
+                return Float.parseFloat(val);
+
+            if (Double.class == cls || Double.TYPE == cls)
+                return Double.parseDouble(val);
+
+            if (BigDecimal.class == cls)
+                return new BigDecimal(val);
+
+            if (Collection.class == cls)
+                return Arrays.asList(val.split(";"));
+
+            if (Set.class == cls)
+                return new HashSet<>(Arrays.asList(val.split(";")));
+
+            if (Object[].class == cls)
+                return val.split(";");
+
+            if (byte[].class == cls) {
+                String[] els = val.split(";");
+
+                if (els.length == 0 || (els.length == 1 && els[0].length() == 0))
+                    return new byte[0];
+
+                byte[] res = new byte[els.length];
+
+                for (int i = 0; i < els.length; i ++)
+                    res[i] =  Byte.valueOf(els[i]);
+
+                return res;
+            }
+
+            return val;
+        }
+
+        /**
+         * Check if class is not a complex bean.
+         *
+         * @param cls Target class.
+         * @return {@code True} if class is primitive or build-in java type or IgniteUuid.
+         */
+        private static boolean isBuildInObject(Class cls) {
+            return cls.isPrimitive() || cls.getName().startsWith("java.") ||
+                IgniteUuid.class == cls || IgniteBiTuple.class == cls || GridTuple3.class == cls;
+        }
+
+        /** {@inheritDoc} */
+        @SuppressWarnings("unchecked")
+        @Override public Object execute() throws IgniteException {
+            String nidsArg = argument(0);
+            String taskName = argument(1);
+
+            Object jobArgs = null;
+
+            if (argsCnt > 2) {
+                String argClsName = argument(2);
+
+                assert argClsName != null;
+
+                try {
+                    Class<?> argCls = Class.forName(argClsName);
+
+                    if (argCls == Void.class)
+                        jobArgs = null;
+                    else if (isBuildInObject(argCls))
+                        jobArgs = toObject(argCls, 3);
+                    else {
+                        int beanArgsCnt = argsCnt - 3;
+
+                        for (Constructor ctor : argCls.getDeclaredConstructors()) {
+                            Class[] types = ctor.getParameterTypes();
+
+                            if (types.length == beanArgsCnt) {
+                                Object[] initargs = new Object[beanArgsCnt];
+
+                                for (int i = 0; i < beanArgsCnt; i++) {
+                                    String val = argument(i + 3);
+
+                                    initargs[i] = toSimpleObject(types[i], val);
+                                }
+
+                                jobArgs = ctor.newInstance(initargs);
+
+                                break;
+                            }
+                        }
+
+                        if (jobArgs == null)
+                            throw new IgniteException("Failed to execute task [task name=" + taskName + "]");
+                    }
+                }
+                catch (Exception e) {
+                    throw new IgniteException("Failed to execute task [task name=" + taskName + "]", e);
+                }
+            }
+
+            final Collection<UUID> nids;
+
+            if (nidsArg == null || nidsArg.equals("null") || nidsArg.equals("")) {
+                Collection<ClusterNode> nodes = ignite.cluster().nodes();
+
+                nids = new ArrayList<>(nodes.size());
+
+                for (ClusterNode node : nodes)
+                    nids.add(node.id());
+            }
+            else {
+                String[] items = nidsArg.split(";");
+
+                nids = new ArrayList<>(items.length);
+
+                for (String item : items) {
+                    try {
+                        nids.add(UUID.fromString(item));
+                    } catch (IllegalArgumentException ignore) {
+                        // No-op.
+                    }
+                }
+            }
+
+            return ignite.compute().execute(taskName, new VisorTaskArgument<>(nids, jobArgs, false));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
index 9be8c35..7eebbf1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/util/VisorTaskUtils.java
@@ -70,6 +70,7 @@ import org.jetbrains.annotations.Nullable;
 
 import static java.lang.System.getProperty;
 import static org.apache.ignite.configuration.FileSystemConfiguration.DFLT_IGFS_LOG_DIR;
+import static org.apache.ignite.events.EventType.EVTS_DISCOVERY;
 import static org.apache.ignite.events.EventType.EVT_CLASS_DEPLOY_FAILED;
 import static org.apache.ignite.events.EventType.EVT_JOB_CANCELLED;
 import static org.apache.ignite.events.EventType.EVT_JOB_FAILED;
@@ -392,8 +393,13 @@ public class VisorTaskUtils {
      */
     public static Collection<VisorGridEvent> collectEvents(Ignite ignite, String evtOrderKey, String evtThrottleCntrKey,
         final boolean all) {
-        return collectEvents(ignite, evtOrderKey, evtThrottleCntrKey, all ? VISOR_ALL_EVTS : VISOR_NON_TASK_EVTS,
-            EVT_MAPPER);
+        int[] evtTypes = all ? VISOR_ALL_EVTS : VISOR_NON_TASK_EVTS;
+
+        // Collect discovery events for Web Console.
+        if (evtOrderKey.startsWith("CONSOLE_"))
+            evtTypes = concat(evtTypes, EVTS_DISCOVERY);
+
+        return collectEvents(ignite, evtOrderKey, evtThrottleCntrKey, evtTypes, EVT_MAPPER);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
----------------------------------------------------------------------
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
index 0adbc14..c2795a4 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
@@ -17,42 +17,64 @@
 
 package org.apache.ignite.internal.processors.rest.protocols.http.jetty;
 
+import java.lang.reflect.Method;
 import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import net.sf.json.JSONObject;
 import net.sf.json.JsonConfig;
 import net.sf.json.processors.JsonBeanProcessor;
 import net.sf.json.processors.JsonBeanProcessorMatcher;
 import net.sf.json.processors.JsonValueProcessor;
-
-import java.util.*;
 import net.sf.json.processors.JsonValueProcessorMatcher;
+import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlIndexMetadata;
 import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.visor.cache.VisorCache;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteUuid;
 
 /**
  * Jetty protocol json configuration.
  */
-public class GridJettyJsonConfig extends JsonConfig {
+class GridJettyJsonConfig extends JsonConfig {
+    /** Logger. */
+    private final IgniteLogger log;
+
     /**
-     * Constructs default jetty json config.
+     * Class for finding a matching JsonBeanProcessor.
      */
-    public GridJettyJsonConfig() {
-        registerJsonValueProcessor(UUID.class, new UUIDToStringJsonProcessor());
-        registerJsonValueProcessor(Date.class, new DateToStringJsonProcessor());
-        registerJsonValueProcessor(java.sql.Date.class, new DateToStringJsonProcessor());
-
-        registerJsonBeanProcessor(GridCacheSqlMetadata.class, new GridCacheSqlMetadataBeanProcessor());
-        registerJsonValueProcessor(GridCacheSqlIndexMetadata.class, new GridCacheSqlIndexMetadataToJson());
+    private static final JsonBeanProcessorMatcher LESS_NAMING_BEAN_MATCHER = new JsonBeanProcessorMatcher() {
+        /** {@inheritDoc} */
+        @Override public Object getMatch(Class target, Set keys) {
+            return GridJettyJsonConfig.getMatch(target, keys);
+        }
+    };
 
-        setJsonBeanProcessorMatcher(new GridJettyJsonBeanProcessorMatcher());
-        setJsonValueProcessorMatcher(new GridJettyJsonValueProcessorMatcher());
-    }
+    /**
+     * Class for finding a matching JsonValueProcessor.
+     */
+    private static final JsonValueProcessorMatcher LESS_NAMING_VALUE_MATCHER = new JsonValueProcessorMatcher() {
+        /** {@inheritDoc} */
+        @Override public Object getMatch(Class target, Set keys) {
+            return GridJettyJsonConfig.getMatch(target, keys);
+        }
+    };
 
     /**
      * Helper class for simple to-string conversion for {@link UUID}.
      */
-    private static class UUIDToStringJsonProcessor implements JsonValueProcessor {
+    private static JsonValueProcessor UUID_PROCESSOR = new JsonValueProcessor() {
         /** {@inheritDoc} */
         @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) {
             if (val == null)
@@ -68,128 +90,228 @@ public class GridJettyJsonConfig extends JsonConfig {
         @Override public Object processObjectValue(String key, Object val, JsonConfig jsonCfg) {
             return processArrayValue(val, jsonCfg);
         }
-    }
+    };
 
     /**
-     * Helper class for simple to-string conversion for {@link Date}.
+     * Helper class for simple to-string conversion for {@link UUID}.
      */
-    private static class DateToStringJsonProcessor implements JsonValueProcessor {
-        private final DateFormat enUsFormat
-            =  DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
-
+    private static JsonValueProcessor IGNITE_BI_TUPLE_PROCESSOR = new JsonValueProcessor() {
         /** {@inheritDoc} */
-        @Override public synchronized Object processArrayValue(Object val, JsonConfig jsonCfg) {
+        @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) {
             if (val == null)
                 return new JSONObject(true);
 
-            if (val instanceof Date)
-                return enUsFormat.format(val);
+            if (val instanceof IgniteBiTuple) {
+                IgniteBiTuple t2 = (IgniteBiTuple)val;
+
+                final JSONObject ret = new JSONObject();
+
+                ret.element("key", t2.getKey(), jsonCfg);
+                ret.element("value", t2.getValue(), jsonCfg);
+
+                return ret;
+            }
 
             throw new UnsupportedOperationException("Serialize value to json is not supported: " + val);
         }
 
         /** {@inheritDoc} */
-        @Override public synchronized Object processObjectValue(String key, Object val, JsonConfig jsonCfg) {
+        @Override public Object processObjectValue(String key, Object val, JsonConfig jsonCfg) {
             return processArrayValue(val, jsonCfg);
         }
-    }
+    };
 
     /**
-     * Helper class for simple to-json conversion for {@link GridCacheSqlMetadata}.
+     * Helper class for simple to-string conversion for {@link IgniteUuid}.
      */
-    private static class GridCacheSqlMetadataBeanProcessor implements JsonBeanProcessor {
+    private static JsonValueProcessor IGNITE_UUID_PROCESSOR = new JsonValueProcessor() {
         /** {@inheritDoc} */
-        @Override public JSONObject processBean(Object bean, JsonConfig jsonCfg) {
-            if (bean == null)
+        @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) {
+            if (val == null)
                 return new JSONObject(true);
 
-            if (bean instanceof GridCacheSqlMetadata) {
-                GridCacheSqlMetadata r = (GridCacheSqlMetadata) bean;
+            if (val instanceof IgniteUuid)
+                return val.toString();
 
-                return new JSONObject()
-                    .element("cacheName", r.cacheName(), jsonCfg)
-                    .element("types", r.types(), jsonCfg)
-                    .element("keyClasses", r.keyClasses(), jsonCfg)
-                    .element("valClasses", r.valClasses(), jsonCfg)
-                    .element("fields", r.fields(), jsonCfg)
-                    .element("indexes", r.indexes(), jsonCfg);
-            }
+            throw new UnsupportedOperationException("Serialize value to json is not supported: " + val);
+        }
 
-            throw new UnsupportedOperationException("Serialize bean to json is not supported: " + bean);
+        /** {@inheritDoc} */
+        @Override public Object processObjectValue(String key, Object val, JsonConfig jsonCfg) {
+            return processArrayValue(val, jsonCfg);
         }
-    }
+    };
 
     /**
-     * Helper class for simple to-json conversion for {@link GridCacheSqlIndexMetadata}.
+     * Helper class for simple to-string conversion for {@link Date}.
      */
-    private static class GridCacheSqlIndexMetadataToJson implements JsonValueProcessor {
+    private static JsonValueProcessor DATE_PROCESSOR = new JsonValueProcessor() {
+        /** Thread local US date format. */
+        private final ThreadLocal<DateFormat> dateFmt = new ThreadLocal<DateFormat>() {
+            @Override protected DateFormat initialValue() {
+                return DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
+            }
+        };
+
         /** {@inheritDoc} */
-        @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) {
+        @Override public synchronized Object processArrayValue(Object val, JsonConfig jsonCfg) {
             if (val == null)
                 return new JSONObject(true);
 
-            if (val instanceof GridCacheSqlIndexMetadata) {
-                GridCacheSqlIndexMetadata r = (GridCacheSqlIndexMetadata) val;
-
-                return new JSONObject()
-                    .element("name", r.name())
-                    .element("fields", r.fields())
-                    .element("descendings", r.descendings())
-                    .element("unique", r.unique());
-            }
+            if (val instanceof Date)
+                return dateFmt.get().format(val);
 
-            throw new UnsupportedOperationException("Serialize array to string is not supported: " + val);
+            throw new UnsupportedOperationException("Serialize value to json is not supported: " + val);
         }
 
         /** {@inheritDoc} */
-        @Override public Object processObjectValue(String key, Object value, JsonConfig jsonCfg) {
-            return processArrayValue(value, jsonCfg);
+        @Override public synchronized Object processObjectValue(String key, Object val, JsonConfig jsonCfg) {
+            return processArrayValue(val, jsonCfg);
         }
-    }
+    };
 
     /**
-     * Class for finding a matching JsonBeanProcessor. Matches the target class with instanceOf.
+     * Constructs default jetty json config.
      */
-    private static final class GridJettyJsonBeanProcessorMatcher extends JsonBeanProcessorMatcher {
-        /** {@inheritDoc} */
-        @Override public Object getMatch(Class target, Set keys) {
-            if (target == null || keys == null)
-                return null;
+    GridJettyJsonConfig(IgniteLogger log) {
+        this.log = log;
 
-            if (keys.contains(target))
-                return target;
+        setAllowNonStringKeys(true);
 
-            for (Object key : keys) {
-                Class<?> clazz = (Class<?>) key;
+        registerJsonValueProcessor(IgniteBiTuple.class, IGNITE_BI_TUPLE_PROCESSOR);
+        registerJsonValueProcessor(UUID.class, UUID_PROCESSOR);
+        registerJsonValueProcessor(IgniteUuid.class, IGNITE_UUID_PROCESSOR);
+        registerJsonValueProcessor(Date.class, DATE_PROCESSOR);
+        registerJsonValueProcessor(java.sql.Date.class, DATE_PROCESSOR);
 
-                if (clazz.isAssignableFrom(target))
-                    return key;
-            }
+        final LessNamingProcessor lessNamingProcessor = new LessNamingProcessor();
+
+        registerJsonBeanProcessor(LessNamingProcessor.class, lessNamingProcessor);
+        registerJsonValueProcessor(LessNamingProcessor.class, lessNamingProcessor);
+
+        setJsonBeanProcessorMatcher(LESS_NAMING_BEAN_MATCHER);
+        setJsonValueProcessorMatcher(LESS_NAMING_VALUE_MATCHER);
+    }
 
+    /**
+     * Returns the matching class calculated with the target class and the provided set. Matches the target class with
+     * instanceOf, for Visor classes return custom processor class.
+     *
+     * @param target the target class to match
+     * @param keys a set of possible matches
+     */
+    private static Object getMatch(Class target, Set keys) {
+        if (target == null || keys == null)
             return null;
+
+        if (target.getSimpleName().startsWith("Visor") ||
+            GridCacheSqlMetadata.class.isAssignableFrom(target) ||
+            GridCacheSqlIndexMetadata.class.isAssignableFrom(target))
+            return LessNamingProcessor.class;
+
+        if (keys.contains(target))
+            return target;
+
+        for (Object key : keys) {
+            Class<?> clazz = (Class<?>)key;
+
+            if (clazz.isAssignableFrom(target))
+                return key;
         }
+
+        return null;
     }
 
     /**
-     * Class for finding a matching JsonValueProcessor. Matches the target class with instanceOf.
+     * Helper class for simple to-json conversion for Visor classes.
      */
-    private static final class GridJettyJsonValueProcessorMatcher extends JsonValueProcessorMatcher {
+    private class LessNamingProcessor implements JsonBeanProcessor, JsonValueProcessor {
+        /** Methods to exclude. */
+        private final Collection<String> exclMtds = Arrays.asList("toString", "hashCode", "clone", "getClass");
+
+        /** */
+        private final Map<Class<?>, Collection<Method>> clsCache = new HashMap<>();
+
+        /** */
+        private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
+
         /** {@inheritDoc} */
-        @Override public Object getMatch(Class target, Set keys) {
-            if (target == null || keys == null)
-                return null;
+        @Override public JSONObject processBean(Object bean, JsonConfig jsonCfg) {
+            if (bean == null)
+                return new JSONObject(true);
 
-            if (keys.contains(target))
-                return target;
+            final JSONObject ret = new JSONObject();
 
-            for (Object key : keys) {
-                Class<?> clazz = (Class<?>) key;
+            Collection<Method> methods;
 
-                if (clazz.isAssignableFrom(target))
-                    return key;
+            Class<?> cls = bean.getClass();
+
+            // Get descriptor from cache.
+            rwLock.readLock().lock();
+
+            try {
+                methods = clsCache.get(cls);
+            }
+            finally {
+                rwLock.readLock().unlock();
             }
 
-            return null;
+            // If missing in cache - build descriptor
+            if (methods == null) {
+                Method[] publicMtds = cls.getMethods();
+
+                methods = new ArrayList<>(publicMtds.length);
+
+                for (Method mtd : publicMtds) {
+                    Class retType = mtd.getReturnType();
+
+                    if (mtd.getParameterTypes().length != 0 ||
+                        retType == void.class ||
+                        retType == cls ||
+                        exclMtds.contains(mtd.getName()) ||
+                        (retType == VisorCache.class && mtd.getName().equals("history")))
+                        continue;
+
+                    mtd.setAccessible(true);
+
+                    methods.add(mtd);
+                }
+
+                /*
+                 * Allow multiple puts for the same class - they will simply override.
+                 */
+                rwLock.writeLock().lock();
+
+                try {
+                    clsCache.put(cls, methods);
+                }
+                finally {
+                    rwLock.writeLock().unlock();
+                }
+            }
+
+            // Extract fields values using descriptor and build JSONObject.
+            for (Method mtd : methods) {
+                try {
+                    ret.element(mtd.getName(), mtd.invoke(bean), jsonCfg);
+                }
+                catch (Exception e) {
+                    U.error(log, "Failed to read object property [type= " + cls.getName()
+                        + ", property=" + mtd.getName() + "]", e);
+                }
+            }
+
+            return ret;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) {
+            return processBean(val, jsonCfg);
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object processObjectValue(String key, Object val, JsonConfig jsonCfg) {
+            return processArrayValue(val, jsonCfg);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
----------------------------------------------------------------------
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
index 5f2c4ba..b6a386b 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
@@ -368,7 +368,7 @@ public class GridJettyRestHandler extends AbstractHandler {
                 throw (Error)e;
         }
 
-        JsonConfig cfg = new GridJettyJsonConfig();
+        JsonConfig cfg = new GridJettyJsonConfig(log);
 
         // Workaround for not needed transformation of string into JSON object.
         if (cmdRes.getResponse() instanceof String)

http://git-wip-us.apache.org/repos/asf/ignite/blob/0c3f5d9b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java
----------------------------------------------------------------------
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java
index 0440783..9caadf4 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java
@@ -280,7 +280,7 @@ public class GridJettyRestProtocol extends GridRestProtocolAdapter {
             int srvPort;
 
             try {
-                srvPort = Integer.valueOf(srvPortStr);
+                srvPort = Integer.parseInt(srvPortStr);
             }
             catch (NumberFormatException ignore) {
                 throw new IgniteCheckedException("Failed to start Jetty server because IGNITE_JETTY_PORT system property " +


Mime
View raw message