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/master Commit: 0c3f5d9b72ce7c911bb9e71ae99b043924318002 Parents: dcfd30e Author: AKuznetsov <[email protected]> Authored: Mon May 16 18:45:33 2016 +0700 Committer: AKuznetsov <[email protected]> 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 " +
