Repository: ignite Updated Branches: refs/heads/master ad0cd5043 -> 3ba73ca43
IGNITE-3345 REST: Added support for Java built in types (boolean, int, long, ..., UUID) for put/get operations. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/3ba73ca4 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/3ba73ca4 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/3ba73ca4 Branch: refs/heads/master Commit: 3ba73ca43f9d792d6304eb04d706dcd13a795a5f Parents: ad0cd50 Author: Alexey Kuznetsov <akuznet...@apache.org> Authored: Tue Feb 20 10:32:27 2018 +0700 Committer: Alexey Kuznetsov <akuznet...@apache.org> Committed: Tue Feb 20 10:32:27 2018 +0700 ---------------------------------------------------------------------- .../JettyRestProcessorAbstractSelfTest.java | 342 +++++++++++++++++++ .../http/jetty/GridJettyRestHandler.java | 105 +++++- 2 files changed, 436 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/3ba73ca4/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 a33f019..e4433cf 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 @@ -27,6 +27,9 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; import java.text.DateFormat; import java.util.Collection; import java.util.HashMap; @@ -152,6 +155,8 @@ import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; import static org.apache.ignite.internal.IgniteVersionUtils.VER_STR; import static org.apache.ignite.internal.processors.query.QueryUtils.TEMPLATE_PARTITIONED; import static org.apache.ignite.internal.processors.query.QueryUtils.TEMPLATE_REPLICATED; +import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_FAILED; +import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_SUCCESS; /** * Tests for Jetty REST protocol. @@ -2142,6 +2147,340 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro } /** + * @return Cache. + */ + protected <K, V> IgniteCache<K, V> typedCache() { + return grid(0).cache("test_typed_access"); + } + + /** + * @param type Key and value type. + * @param k Key to put. + * @param v Value to put. + * @param status Expected operation status to check. + * @throws Exception If failed. + */ + private void putTypedValue(String type, String k, String v, int status) throws Exception { + String ret = content("test_typed_access", GridRestCommand.CACHE_PUT, + "keyType", type, + "valueType", type, + "key", k, + "val", v + ); + + info("Command result: " + ret); + + JsonNode json = JSON_MAPPER.readTree(ret); + + assertEquals(status, json.get("successStatus").asInt()); + + if (status == STATUS_SUCCESS) + assertTrue(json.get("error").isNull()); + else + assertTrue(json.get("error").asText().startsWith("Failed to convert value to specified type [type=")); + } + + /** + * @throws Exception If failed. + */ + public void testTypedPut() throws Exception { + // Test boolean type. + putTypedValue("boolean", "true", "false", STATUS_SUCCESS); + putTypedValue("java.lang.Boolean", "false", "true", STATUS_SUCCESS); + + IgniteCache<Boolean, Boolean> cBool = typedCache(); + + assertEquals(cBool.get(true), Boolean.FALSE); + assertEquals(cBool.get(false), Boolean.TRUE); + + // Test byte type. + putTypedValue("byte", "64", "100", STATUS_SUCCESS); + putTypedValue("java.lang.Byte", "-25", "-127", STATUS_SUCCESS); + putTypedValue("byte", "65", "aaa", STATUS_FAILED); + putTypedValue("byte", "aaa", "64", STATUS_FAILED); + putTypedValue("byte", "aaa", "aaa", STATUS_FAILED); + + IgniteCache<Byte, Byte> cByte = typedCache(); + + assertEquals(cByte.get(Byte.valueOf("64")), Byte.valueOf("100")); + assertEquals(cByte.get(Byte.valueOf("-25")), Byte.valueOf("-127")); + + // Test short type. + putTypedValue("short", "1024", "4096", STATUS_SUCCESS); + putTypedValue("java.lang.Short", "-15000", "-16000", STATUS_SUCCESS); + putTypedValue("short", "1025", "bbb", STATUS_FAILED); + putTypedValue("short", "bbb", "5", STATUS_FAILED); + putTypedValue("short", "bbb", "bbb", STATUS_FAILED); + + IgniteCache<Short, Short> cShort = typedCache(); + + assertEquals(cShort.get(Short.valueOf("1024")), Short.valueOf("4096")); + assertEquals(cShort.get(Short.valueOf("-15000")), Short.valueOf("-16000")); + + // Test integer type. + putTypedValue("int", "65555", "128256", STATUS_SUCCESS); + putTypedValue("Integer", "74555", "200000", STATUS_SUCCESS); + putTypedValue("java.lang.Integer", "-200", "-100000", STATUS_SUCCESS); + putTypedValue("int", "0", "ccc", STATUS_FAILED); + putTypedValue("int", "ccc", "0", STATUS_FAILED); + putTypedValue("int", "ccc", "ccc", STATUS_FAILED); + + IgniteCache<Integer, Integer> cInt = typedCache(); + + assertEquals(cInt.get(65555), Integer.valueOf(128256)); + assertEquals(cInt.get(74555), Integer.valueOf(200000)); + assertEquals(cInt.get(-200), Integer.valueOf(-100000)); + + // Test long type. + putTypedValue("long", "3000000", "400000", STATUS_SUCCESS); + putTypedValue("java.lang.Long", "-3000000", "-400000", STATUS_SUCCESS); + putTypedValue("long", "777", "ddd", STATUS_FAILED); + putTypedValue("long", "ddd", "777", STATUS_FAILED); + putTypedValue("long", "ddd", "ddd", STATUS_FAILED); + + IgniteCache<Long, Long> cLong = typedCache(); + + assertEquals(cLong.get(3000000L), Long.valueOf(400000)); + assertEquals(cLong.get(-3000000L), Long.valueOf(-400000)); + + // Test float type. + putTypedValue("float", "1.5", "2.5", STATUS_SUCCESS); + putTypedValue("java.lang.Float", "-7.5", "-8.5", STATUS_SUCCESS); + putTypedValue("float", "1.5", "hhh", STATUS_FAILED); + putTypedValue("float", "hhh", "1.5", STATUS_FAILED); + putTypedValue("float", "hhh", "hhh", STATUS_FAILED); + + IgniteCache<Float, Float> cFloat = typedCache(); + + assertEquals(cFloat.get(1.5f), 2.5f); + assertEquals(cFloat.get(-7.5f), -8.5f); + + // Test double type. + putTypedValue("double", "5.5", "75.5", STATUS_SUCCESS); + putTypedValue("java.lang.Double", "-155.5", "-255.5", STATUS_SUCCESS); + putTypedValue("double", "jjj", "75.5", STATUS_FAILED); + putTypedValue("double", "6.5", "jjj", STATUS_FAILED); + putTypedValue("double", "jjj", "jjj", STATUS_FAILED); + + IgniteCache<Double, Double> cDouble = typedCache(); + + assertEquals(cDouble.get(5.5d), 75.5d); + assertEquals(cDouble.get(-155.5d), -255.5d); + + // Test date type. + putTypedValue("date", "2018-02-18", "2017-01-01", STATUS_SUCCESS); + putTypedValue("java.sql.Date", "2018-01-01", "2017-02-02", STATUS_SUCCESS); + putTypedValue("date", "xxxx-yy-mm", "2017-01-01", STATUS_FAILED); + putTypedValue("date", "2018-03-18", "xxxx-yy-mm", STATUS_FAILED); + putTypedValue("date", "xxxx-yy-mm", "xxxx-yy-mm", STATUS_FAILED); + + IgniteCache<Date, Date> cDate = typedCache(); + + assertEquals(cDate.get(Date.valueOf("2018-02-18")), Date.valueOf("2017-01-01")); + assertEquals(cDate.get(Date.valueOf("2018-01-01")), Date.valueOf("2017-02-02")); + + // Test time type. + putTypedValue("Time", "01:01:01", "02:02:02", STATUS_SUCCESS); + putTypedValue("java.sql.Time", "03:03:03", "04:04:04", STATUS_SUCCESS); + putTypedValue("Time", "aa:bb:dd", "02:02:02", STATUS_FAILED); + putTypedValue("Time", "01:01:01", "zz:vv:pp", STATUS_FAILED); + putTypedValue("Time", "zz:zz:zz", "zz:zz:zz", STATUS_FAILED); + + IgniteCache<Time, Time> cTime = typedCache(); + + assertEquals(cTime.get(Time.valueOf("01:01:01")), Time.valueOf("02:02:02")); + assertEquals(cTime.get(Time.valueOf("03:03:03")), Time.valueOf("04:04:04")); + + // Test timestamp type. + putTypedValue("Timestamp", "2018-02-18%2001:01:01", "2017-01-01%2002:02:02", STATUS_SUCCESS); + putTypedValue("java.sql.timestamp", "2018-01-01%2001:01:01", "2018-05-05%2005:05:05", STATUS_SUCCESS); + putTypedValue("timestamp", "error", "2018-03-18%2001:01:01", STATUS_FAILED); + putTypedValue("timestamp", "2018-03-18%2001:01:01", "error", STATUS_FAILED); + putTypedValue("timestamp", "error", "error", STATUS_FAILED); + + IgniteCache<Timestamp, Timestamp> cTimestamp = typedCache(); + + assertEquals(cTimestamp.get(Timestamp.valueOf("2018-02-18 01:01:01")), Timestamp.valueOf("2017-01-01 02:02:02")); + assertEquals(cTimestamp.get(Timestamp.valueOf("2018-01-01 01:01:01")), Timestamp.valueOf("2018-05-05 05:05:05")); + + // Test UUID type. + UUID k1 = UUID.fromString("121f5ae8-148d-11e8-b642-0ed5f89f718b"); + UUID v1 = UUID.fromString("64c6c225-b31c-4000-b136-ef14562ac785"); + putTypedValue("UUID", k1.toString(), v1.toString(), STATUS_SUCCESS); + putTypedValue("UUID", "error", v1.toString(), STATUS_FAILED); + putTypedValue("UUID", k1.toString(), "error", STATUS_FAILED); + putTypedValue("UUID", "error", "error", STATUS_FAILED); + + UUID k2 = UUID.randomUUID(); + UUID v2 = UUID.randomUUID(); + putTypedValue("java.util.UUID", k2.toString(), v2.toString(), STATUS_SUCCESS); + + IgniteCache<UUID, UUID> cUUID = typedCache(); + + assertEquals(cUUID.get(k1), v1); + assertEquals(cUUID.get(k2), v2); + + // Test IgniteUuid type. + IgniteUuid ik1 = IgniteUuid.randomUuid(); + IgniteUuid iv1 = IgniteUuid.randomUuid(); + putTypedValue("IgniteUuid", ik1.toString(), iv1.toString(), STATUS_SUCCESS); + putTypedValue("IgniteUuid", "error", iv1.toString(), STATUS_FAILED); + putTypedValue("IgniteUuid", ik1.toString(), "error", STATUS_FAILED); + putTypedValue("IgniteUuid", "error", "error", STATUS_FAILED); + + IgniteUuid ik2 = IgniteUuid.randomUuid(); + IgniteUuid iv2 = IgniteUuid.randomUuid(); + putTypedValue("org.apache.ignite.lang.IgniteUuid", ik2.toString(), iv2.toString(), STATUS_SUCCESS); + + IgniteCache<IgniteUuid, IgniteUuid> cIgniteUUID = typedCache(); + + assertEquals(cIgniteUUID.get(ik1), iv1); + assertEquals(cIgniteUUID.get(ik2), iv2); + } + + /** + * @param keyType Key type. + * @param k Key value. + * @param exp Expected value to test. + * @throws Exception If failed. + */ + private void getTypedValue(String keyType, String k, String exp) throws Exception { + String ret = content("test_typed_access", GridRestCommand.CACHE_GET, + "keyType", keyType, + "key", k + ); + + info("Command result: " + ret); + + assertEquals(exp, jsonResponse(ret).asText()); + } + + /** + * @throws Exception If failed. + */ + public void testTypedGet() throws Exception { + // Test boolean type. + IgniteCache<Boolean, Boolean> cBool = typedCache(); + + cBool.put(true, false); + cBool.put(false, true); + + getTypedValue("boolean", "true", "false"); + getTypedValue("java.lang.Boolean", "false", "true"); + + // Test byte type. + IgniteCache<Byte, Byte> cByte = typedCache(); + + cByte.put((byte)77, (byte)55); + cByte.put((byte)-88, (byte)-10); + + getTypedValue("byte", "77", "55"); + getTypedValue("java.lang.Byte", "-88", "-10"); + + // Test short type. + IgniteCache<Short, Short> cShort = typedCache(); + + cShort.put((short)2222, (short)3333); + cShort.put((short)-11111, (short)-12222); + + getTypedValue("short", "2222", "3333"); + getTypedValue("java.lang.Short", "-11111", "-12222"); + + // Test integer type. + IgniteCache<Integer, Integer> cInt = typedCache(); + cInt.put(65555, 128256); + cInt.put(74555, 200000); + cInt.put(-200, -100000); + + getTypedValue("int", "65555", "128256"); + getTypedValue("Integer", "74555", "200000"); + getTypedValue("java.lang.Integer", "-200", "-100000"); + + // Test long type. + IgniteCache<Long, Long> cLong = typedCache(); + + cLong.put(3333333L, 4444444L); + cLong.put(-3333333L, -4444444L); + + getTypedValue("long", "3333333", "4444444"); + getTypedValue("java.lang.Long", "-3333333", "-4444444"); + + // Test float type. + IgniteCache<Float, Float> cFloat = typedCache(); + + cFloat.put(11.5f, 21.5f); + cFloat.put(-71.5f, -81.5f); + + getTypedValue("float", "11.5", "21.5"); + getTypedValue("java.lang.Float", "-71.5", "-81.5"); + + // Test double type. + IgniteCache<Double, Double> cDouble = typedCache(); + + cDouble.put(58.5d, 758.5d); + cDouble.put(-1558.5d, -2558.5d); + + getTypedValue("double", "58.5", "758.5"); + getTypedValue("java.lang.Double", "-1558.5", "-2558.5"); + + // Test date type. + IgniteCache<Date, Date> cDate = typedCache(); + + cDate.put(Date.valueOf("2018-02-18"), Date.valueOf("2017-01-01")); + cDate.put(Date.valueOf("2018-01-01"), Date.valueOf("2017-02-02")); + + getTypedValue("Date", "2018-02-18", "2017-01-01"); + getTypedValue("java.sql.Date", "2018-01-01", "2017-02-02"); + + // Test time type. + IgniteCache<Time, Time> cTime = typedCache(); + + cTime.put(Time.valueOf("01:01:01"), Time.valueOf("02:02:02")); + cTime.put(Time.valueOf("03:03:03"), Time.valueOf("04:04:04")); + + getTypedValue("Time", "01:01:01", "02:02:02"); + getTypedValue("java.sql.Time", "03:03:03", "04:04:04"); + + // Test timestamp type. + IgniteCache<Timestamp, String> cTimestamp = typedCache(); + + cTimestamp.put(Timestamp.valueOf("2018-02-18 01:01:01"), "test1"); + cTimestamp.put(Timestamp.valueOf("2018-01-01 01:01:01"), "test2"); + + getTypedValue("Timestamp", "2018-02-18%2001:01:01", "test1"); + getTypedValue("java.sql.timestamp", "2018-01-01%2001:01:01", "test2"); + + // Test UUID type. + IgniteCache<UUID, UUID> cUUID = typedCache(); + + UUID k1 = UUID.fromString("121f5ae8-148d-11e8-b642-0ed5f89f718b"); + UUID v1 = UUID.fromString("64c6c225-b31c-4000-b136-ef14562ac785"); + cUUID.put(k1, v1); + + UUID k2 = UUID.randomUUID(); + UUID v2 = UUID.randomUUID(); + cUUID.put(k2, v2); + + getTypedValue("UUID", k1.toString(), v1.toString()); + getTypedValue("java.util.UUID", k2.toString(), v2.toString()); + + + // Test IgniteUuid type. + IgniteCache<IgniteUuid, IgniteUuid> cIgniteUUID = typedCache(); + + IgniteUuid ik1 = IgniteUuid.randomUuid(); + IgniteUuid iv1 = IgniteUuid.randomUuid(); + cIgniteUUID.put(ik1, iv1); + + IgniteUuid ik2 = IgniteUuid.randomUuid(); + IgniteUuid iv2 = IgniteUuid.randomUuid(); + cIgniteUUID.put(ik2, iv2); + + getTypedValue("IgniteUuid", ik1.toString(), iv1.toString()); + getTypedValue("org.apache.ignite.lang.IgniteUuid", ik2.toString(), iv2.toString()); + } + + /** * @return Signature. * @throws Exception If failed. */ @@ -2171,6 +2510,9 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro * Init cache. */ protected void initCache() { + CacheConfiguration typedCache = new CacheConfiguration<>("test_typed_access"); + ignite(0).getOrCreateCache(typedCache); + CacheConfiguration<Integer, Organization> orgCacheCfg = new CacheConfiguration<>("organization"); orgCacheCfg.setIndexedTypes(Integer.class, Organization.class); http://git-wip-us.apache.org/repos/asf/ignite/blob/3ba73ca4/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 7bad10d..0b24998 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 @@ -25,6 +25,9 @@ import java.io.InputStreamReader; import java.io.LineNumberReader; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; @@ -54,6 +57,7 @@ import org.apache.ignite.internal.processors.rest.request.RestQueryRequest; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteClosure; +import org.apache.ignite.lang.IgniteUuid; import org.apache.ignite.plugin.security.SecurityCredentials; import org.eclipse.jetty.server.Request; @@ -408,6 +412,81 @@ public class GridJettyRestHandler extends AbstractHandler { } /** + * @param type Optional value type. + * @param obj Object to convert. + * @return Converted value. + * @throws IgniteCheckedException If failed to convert. + */ + private Object convert(String type, Object obj) throws IgniteCheckedException { + if (F.isEmpty(type) || obj == null) + return obj; + + String s = (String)obj; + + try { + switch (type.toLowerCase()) { + case "boolean": + case "java.lang.boolean": + return Boolean.valueOf(s); + + case "byte": + case "java.lang.byte": + return Byte.valueOf(s); + + case "short": + case "java.lang.short": + return Short.valueOf(s); + + case "int": + case "integer": + case "java.lang.integer": + return Integer.valueOf(s); + + case "long": + case "java.lang.long": + return Long.valueOf(s); + + case "float": + case "java.lang.float": + return Float.valueOf(s); + + case "double": + case "java.lang.double": + return Double.valueOf(s); + + case "date": + case "java.sql.date": + return Date.valueOf(s); + + case "time": + case "java.sql.time": + return Time.valueOf(s); + + case "timestamp": + case "java.sql.timestamp": + return Timestamp.valueOf(s); + + case "uuid": + case "java.util.uuid": + return UUID.fromString(s); + + case "igniteuuid": + case "org.apache.ignite.lang.igniteuuid": + return IgniteUuid.fromString(s); + + default: + // No-op. + } + } + catch (Throwable e) { + throw new IgniteCheckedException("Failed to convert value to specified type [type=" + type + + ", val=" + s + ", reason=" + e.getClass().getName() + ": " + e.getMessage() + "]"); + } + + return obj; + } + + /** * Creates REST request. * * @param cmd Command. @@ -527,13 +606,16 @@ public class GridJettyRestHandler extends AbstractHandler { GridRestCacheRequest restReq0 = new GridRestCacheRequest(); String cacheName = (String)params.get(CACHE_NAME_PARAM); - restReq0.cacheName(F.isEmpty(cacheName) ? null : cacheName); - restReq0.key(params.get("key")); - restReq0.value(params.get("val")); - restReq0.value2(params.get("val2")); - Object val1 = params.get("val1"); + String keyType = (String)params.get("keyType"); + String valType = (String)params.get("valueType"); + + restReq0.key(convert(keyType, params.get("key"))); + restReq0.value(convert(valType, params.get("val"))); + restReq0.value2(convert(valType, params.get("val2"))); + + Object val1 = convert(valType, params.get("val1")); if (val1 != null) restReq0.value(val1); @@ -543,8 +625,8 @@ public class GridJettyRestHandler extends AbstractHandler { if (cmd == CACHE_GET_ALL || cmd == CACHE_PUT_ALL || cmd == CACHE_REMOVE_ALL || cmd == CACHE_CONTAINS_KEYS) { - List<Object> keys = values("k", params); - List<Object> vals = values("v", params); + List<Object> keys = values(keyType, "k", params); + List<Object> vals = values(valType, "v", params); if (keys.size() < vals.size()) throw new IgniteCheckedException("Number of keys must be greater or equals to number of values."); @@ -589,7 +671,7 @@ public class GridJettyRestHandler extends AbstractHandler { restReq0.taskId((String)params.get("id")); restReq0.taskName((String)params.get("name")); - restReq0.params(values("p", params)); + restReq0.params(values(null, "p", params)); restReq0.async(Boolean.parseBoolean((String)params.get("async"))); @@ -641,7 +723,7 @@ public class GridJettyRestHandler extends AbstractHandler { restReq0.sqlQuery((String)params.get("qry")); - restReq0.arguments(values("arg", params).toArray()); + restReq0.arguments(values(null, "arg", params).toArray()); restReq0.typeName((String)params.get("type")); @@ -774,11 +856,12 @@ public class GridJettyRestHandler extends AbstractHandler { /** * Gets values referenced by sequential keys, e.g. {@code key1...keyN}. * + * @param type Optional value type. * @param keyPrefix Key prefix, e.g. {@code key} for {@code key1...keyN}. * @param params Parameters map. * @return Values. */ - protected List<Object> values(String keyPrefix, Map<String, Object> params) { + protected List<Object> values(String type, String keyPrefix, Map<String, Object> params) throws IgniteCheckedException { assert keyPrefix != null; List<Object> vals = new LinkedList<>(); @@ -787,7 +870,7 @@ public class GridJettyRestHandler extends AbstractHandler { String key = keyPrefix + i; if (params.containsKey(key)) - vals.add(params.get(key)); + vals.add(convert(type, params.get(key))); else break; }