This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new ecd5498 IGNITE-12857 Add ability to put non-primitive data types via
HTTP-REST - Fixes #7648.
ecd5498 is described below
commit ecd5498e0e826238db6ea4076695ecb1dfef43f3
Author: Pavel Pereslegin <[email protected]>
AuthorDate: Thu Apr 23 12:33:54 2020 +0300
IGNITE-12857 Add ability to put non-primitive data types via HTTP-REST -
Fixes #7648.
Signed-off-by: Aleksey Plekhanov <[email protected]>
---
.../rest/JettyRestProcessorAbstractSelfTest.java | 783 ++++++++++++++++++++-
.../rest/JettyRestProcessorCommonSelfTest.java | 13 +-
.../processors/query/GridQueryProcessor.java | 11 +
.../rest/handlers/query/QueryCommandHandler.java | 3 +
.../processors/rest/request/RestQueryRequest.java | 17 +
.../http/jetty/GridJettyObjectMapper.java | 79 ++-
.../protocols/http/jetty/GridJettyRestHandler.java | 381 +++++-----
.../http/jetty/GridJettyRestProtocol.java | 2 +-
.../jetty/IgniteBinaryObjectJsonDeserializer.java | 123 ++++
9 files changed, 1205 insertions(+), 207 deletions(-)
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 cfeb90a..2a0aca1 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
@@ -17,27 +17,39 @@
package org.apache.ignite.internal.processors.rest;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
-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.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.ignite.IgniteBinary;
import org.apache.ignite.IgniteCache;
+import org.apache.ignite.binary.BinaryObjectBuilder;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.SqlQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
@@ -158,9 +170,6 @@ import static
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS
*/
@SuppressWarnings("unchecked")
public abstract class JettyRestProcessorAbstractSelfTest extends
JettyRestProcessorCommonSelfTest {
- /** Used to sent request charset. */
- private static final String CHARSET = StandardCharsets.UTF_8.name();
-
/** */
private static boolean memoryMetricsEnabled;
@@ -345,6 +354,310 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
}
/**
+ * Test that after adding the object to the cache, it is available for
query.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testPutJsonQueryEntity() throws Exception {
+ String cacheName = "person";
+
+ Person simple = new Person(100, "John", "Doe", 10000);
+
+ try {
+ putObject(cacheName, "300", simple);
+
+ String ret = content(cacheName, GridRestCommand.CACHE_GET,
"keyType", "int", "key", "300");
+
+ info("Get command result: " + ret);
+
+ checkJson(ret, simple);
+
+ JsonNode val = queryObject(
+ cacheName,
+ GridRestCommand.EXECUTE_SQL_QUERY,
+ "type", Person.class.getSimpleName(),
+ "pageSize", "1",
+ "qry", "orgId = ?",
+ "arg1", String.valueOf(simple.getOrganizationId())
+ );
+
+ assertEquals(simple.getOrganizationId().intValue(),
val.get("orgId").intValue());
+ assertEquals(simple.getSalary(), val.get("salary").doubleValue());
+ assertEquals(simple.getFirstName(),
val.get("firstName").textValue());
+ assertEquals(simple.getLastName(),
val.get("lastName").textValue());
+ } finally {
+ grid(0).cache(cacheName).remove(300);
+ }
+ }
+
+ /**
+ * Test adding a new (unregistered) binary object.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ @SuppressWarnings("ThrowableNotThrown")
+ public void testPutUnregistered() throws Exception {
+ LinkedHashMap<String, String> fields = new LinkedHashMap<>();
+
+ fields.put("id", Long.class.getName());
+ fields.put("name", String.class.getName());
+ fields.put("timestamp", Timestamp.class.getName());
+ fields.put("longs", long[].class.getName());
+ fields.put("igniteUuid", IgniteUuid.class.getName());
+
+ CacheConfiguration<Object, Object> ccfg = new
CacheConfiguration<>("testCache");
+
+ String valType = "SomeNewType";
+
+ ccfg.setQueryEntities(
+ Collections.singletonList(
+ new QueryEntity()
+ .setKeyType("java.lang.Integer")
+ .setValueType(valType)
+ .setFields(fields)
+ .setIndexes(Collections.singleton(new QueryIndex("id"))))
+ );
+
+ IgniteCache cache = grid(0).createCache(ccfg);
+
+ try {
+ List<Integer> list = F.asList(1, 2, 3);
+
+ OuterClass newType = new OuterClass(Long.MAX_VALUE,
"unregistered", 0.1d, list,
+ Timestamp.valueOf("2004-08-26 16:47:03.141592"), new long[]
{Long.MAX_VALUE, -1, Long.MAX_VALUE},
+ UUID.randomUUID(), IgniteUuid.randomUuid(), null);
+
+ putObject(cache.getName(), "300", newType, valType);
+
+ GridTestUtils.assertThrowsWithCause(() -> cache.get(300),
ClassNotFoundException.class);
+
+ assertEquals(newType, getObject(cache.getName(), "300",
OuterClass.class));
+
+ // Sending "optional" (new) field for registered binary type.
+ OuterClass newTypeUpdate = new OuterClass(-1, "update", 0.7d, list,
+ Timestamp.valueOf("2004-08-26 16:47:03.14"), new long[]
{Long.MAX_VALUE, 0, Long.MAX_VALUE},
+ UUID.randomUUID(), IgniteUuid.randomUuid(), new
OuterClass.OptionalObject("test"));
+
+ putObject(cache.getName(), "301", newTypeUpdate, valType);
+
+ assertEquals(newTypeUpdate, getObject(cache.getName(), "301",
OuterClass.class));
+
+ // Check query result.
+ JsonNode res = queryObject(
+ cache.getName(),
+ GridRestCommand.EXECUTE_SQL_QUERY,
+ "type", valType,
+ "pageSize", "1",
+ "keepBinary", "true",
+ "qry", "timestamp < ?",
+ "arg1", "2004-08-26 16:47:03.141"
+ );
+
+ assertEquals(newTypeUpdate, JSON_MAPPER.treeToValue(res,
OuterClass.class));
+
+ // Check fields query result.
+ String qry = "select id, name, timestamp, igniteUuid, longs from "
+ valType +
+ " where timestamp < '2004-08-26 16:47:03.141'";
+
+ res = queryObject(
+ cache.getName(),
+ GridRestCommand.EXECUTE_SQL_FIELDS_QUERY,
+ "keepBinary", "true",
+ "pageSize", "10",
+ "qry", qry
+ );
+
+ assertEquals(5, res.size());
+ assertEquals(newTypeUpdate.id, res.get(0).longValue());
+ assertEquals(newTypeUpdate.name, res.get(1).textValue());
+ assertEquals(newTypeUpdate.timestamp,
Timestamp.valueOf(res.get(2).textValue()));
+ assertEquals(newTypeUpdate.igniteUuid,
IgniteUuid.fromString(res.get(3).textValue()));
+ assertTrue(Arrays.equals(newTypeUpdate.longs,
JSON_MAPPER.treeToValue(res.get(4), long[].class)));
+ } finally {
+ grid(0).destroyCache(ccfg.getName());
+ }
+ }
+
+ /**
+ * Check serialization of the nested binary object.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testPutNestedBinaryObject() throws Exception {
+ IgniteBinary binary = grid(0).binary();
+
+ BinaryObjectBuilder nested = binary.builder("Nested");
+
+ nested.setField("str1", "stringValue");
+ nested.setField("sqlDate", java.sql.Date.valueOf("2019-01-01"));
+
+ // Registering "Nested" type.
+ jcache().put(-1, nested.build());
+
+ BinaryObjectBuilder parent = binary.builder("Parent");
+
+ parent.setField("nested", nested.build());
+ parent.setField("id", Long.MAX_VALUE);
+ parent.setField("byteVal", (byte)1);
+ parent.setField("timestamp", new Timestamp(new
java.util.Date().getTime()));
+
+ // Registering "Parent" type.
+ jcache().put(2, parent.build());
+
+ // Adding another "Parent" object via REST.
+ parent.setField("id", Long.MIN_VALUE);
+
+ String jsonText = JSON_MAPPER.writeValueAsString(parent.build());
+
+ info("Put: " + jsonText);
+
+ String ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_PUT,
+ "keyType", "int",
+ "key", "3",
+ "valueType", "Parent",
+ "val", jsonText
+ );
+
+ info("Put command result: " + ret);
+
+ assertResponseSucceeded(ret, false);
+
+ ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_GET,
"keyType", "int", "key", "3");
+
+ info("Get command result: " + ret);
+
+ JsonNode res = assertResponseSucceeded(ret, false);
+
+ assertEquals(jsonText, JSON_MAPPER.writeValueAsString(res));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testPutComplexObject() throws Exception {
+ String cacheName = "complex";
+
+ DateFormat df = JSON_MAPPER.getDateFormat();
+
+ OuterClass[] objects = new OuterClass[] {
+ new OuterClass(111, "out-0", 0.7d, F.asList(9, 1)),
+ new OuterClass(112, "out-1", 0.1d, F.asList(9, 1)),
+ new OuterClass(113, "out-2", 0.3d, F.asList(9, 1))
+ };
+
+ Complex complex = new Complex(
+ 1234567,
+ "String value",
+ new Timestamp(Calendar.getInstance().getTime().getTime()),
+ Date.valueOf("1986-04-26"),
+ Time.valueOf("23:59:59"),
+ df.parse(df.format(Calendar.getInstance().getTime())),
+ F.asMap("key1", 1, "key2", 2),
+ new int[] {1, 2, 3},
+ F.asList(11, 22, 33).toArray(new Integer[3]),
+ new long[] {Long.MIN_VALUE, 0, Long.MAX_VALUE},
+ F.asList(4, 5, 6),
+ F.asMap(123, null, 4567, null).keySet(),
+ new byte[] {4, 1, 2},
+ new char[] {'a', 'b', 'c'},
+ Color.GREEN,
+ new OuterClass(Long.MIN_VALUE, "outer", 0.7d, F.asList(9, 1)),
+ objects,
+ UUID.randomUUID(),
+ IgniteUuid.randomUuid()
+ );
+
+ putObject(cacheName, "300", complex);
+
+ assertEquals(complex, getObject(cacheName, "300", Complex.class));
+ assertEquals(complex, grid(0).cache(cacheName).get(300));
+
+ // Check query result.
+ JsonNode res = queryObject(
+ cacheName,
+ GridRestCommand.EXECUTE_SQL_QUERY,
+ "type", Complex.class.getSimpleName(),
+ "pageSize", "1",
+ "qry", "sqlDate > ? and sqlDate < ?",
+ "arg1", "1953-03-05",
+ "arg2", "2011-03-11"
+ );
+
+ assertEquals(complex, JSON_MAPPER.treeToValue(res, Complex.class));
+ }
+
+ /**
+ * Test adding an array in JSON format.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testPutJsonArray() throws Exception {
+ Map<String, int[]> map = U.map("1", new int[] {1, 2, 3});
+ putObject(DEFAULT_CACHE_NAME, "1", map, Map.class.getName());
+ assertTrue(Map.class.isAssignableFrom(jcache().get(1).getClass()));
+
+ Set<int[]> set = new HashSet<>(map.values());
+ putObject(DEFAULT_CACHE_NAME, "2", set, Set.class.getName());
+ assertTrue(Set.class.isAssignableFrom(jcache().get(2).getClass()));
+
+ List<int[]> list = new ArrayList<>(set);
+ putObject(DEFAULT_CACHE_NAME, "3", list, Collection.class.getName());
+
assertTrue(Collection.class.isAssignableFrom(jcache().get(3).getClass()));
+
+ int[] ints = {1, 2, 3};
+ putObject(DEFAULT_CACHE_NAME, "4", ints, int[].class.getName());
+ assertTrue(Arrays.equals(ints, (int[])jcache().get(4)));
+
+ Character[] chars = {'a', 'b', 'c'};
+ putObject(DEFAULT_CACHE_NAME, "5", chars, Character[].class.getName());
+ assertTrue(Arrays.equals(chars, (Object[])jcache().get(5)));
+
+ OuterClass[] objects = new OuterClass[] {
+ new OuterClass(111, "out-0", 0.7d, F.asList(9, 1)),
+ new OuterClass(112, "out-1", 0.1d, F.asList(9, 1)),
+ new OuterClass(113, "out-2", 0.3d, F.asList(9, 1))
+ };
+
+ putObject(DEFAULT_CACHE_NAME, "6", objects,
OuterClass[].class.getName());
+ assertTrue(Arrays.equals(objects, (Object[])jcache().get(6)));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testPutIncorrectJson() throws Exception {
+ // Check wrong format.
+ String json = "incorrect JSON format";
+
+ String ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_PUT,
+ "keyType", "int",
+ "key", "5",
+ "valueType", Character[].class.getName(),
+ "val", json
+ );
+
+ assertResponseContainsError(ret, "Failed to convert value to specified
type");
+
+ // Check incorrect type.
+ json = JSON_MAPPER.writeValueAsString(new Character[] {'a', 'b', 'c'});
+
+ ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_PUT,
+ "keyType", "int",
+ "key", "5",
+ "valueType", Person.class.getName(),
+ "val", json
+ );
+
+ assertResponseContainsError(ret, "Failed to convert value to specified
type");
+ }
+
+ /**
* @throws Exception If failed.
*/
@Test
@@ -528,13 +841,15 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
assertCacheOperation(ret, sqlDate.toString());
- jcache().put("timestampKey", new
java.sql.Timestamp(utilDate.getTime()));
+ Timestamp ts = Timestamp.valueOf("2004-08-26 16:47:03.141592653");
+
+ jcache().put("timestampKey", ts);
ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_GET, "key",
"timestampKey");
info("Get timestamp: " + ret);
- assertCacheOperation(ret, date);
+ assertCacheOperation(ret, ts.toString());
}
/**
@@ -1820,7 +2135,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
ret = content(new VisorGatewayArgument(VisorQueryTask.class)
.forNode(locNode)
- .argument(VisorQueryTaskArg.class, "person",
URLEncoder.encode("select * from Person", CHARSET),
+ .argument(VisorQueryTaskArg.class, "person", "select * from
Person",
false, false, false, false, 1));
info("VisorQueryTask result: " + ret);
@@ -1977,8 +2292,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
"</beans>";
ret = content(new VisorGatewayArgument(VisorCacheStartTask.class)
- .argument(VisorCacheStartTaskArg.class, false, "person2",
- URLEncoder.encode(START_CACHE, CHARSET)));
+ .argument(VisorCacheStartTaskArg.class, false, "person2",
START_CACHE));
info("VisorCacheStartTask result: " + ret);
@@ -2099,7 +2413,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
String ret = content("person", GridRestCommand.EXECUTE_SQL_QUERY,
"type", "Person",
"pageSize", "10",
- "qry", URLEncoder.encode(qry, CHARSET),
+ "qry", qry,
"arg1", "1000",
"arg2", "2000"
);
@@ -2172,7 +2486,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
String ret = content(DEFAULT_CACHE_NAME,
GridRestCommand.EXECUTE_SQL_QUERY,
"type", "String",
"pageSize", "1",
- "qry", URLEncoder.encode("select * from String", CHARSET)
+ "qry", "select * from String"
);
JsonNode qryId = validateJsonResponse(ret).get("queryId");
@@ -2219,7 +2533,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
"type", "Person",
"distributedJoins", "true",
"pageSize", "10",
- "qry", URLEncoder.encode(qry, CHARSET),
+ "qry", qry,
"arg1", "o1"
);
@@ -2239,7 +2553,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
String ret = content("person",
GridRestCommand.EXECUTE_SQL_FIELDS_QUERY,
"pageSize", "10",
- "qry", URLEncoder.encode(qry, CHARSET)
+ "qry", qry
);
JsonNode items = validateJsonResponse(ret).get("items");
@@ -2259,7 +2573,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
String ret = content("person",
GridRestCommand.EXECUTE_SQL_FIELDS_QUERY,
"distributedJoins", "true",
"pageSize", "10",
- "qry", URLEncoder.encode(qry, CHARSET)
+ "qry", qry
);
JsonNode items = validateJsonResponse(ret).get("items");
@@ -2278,7 +2592,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
String ret = content("person",
GridRestCommand.EXECUTE_SQL_FIELDS_QUERY,
"pageSize", "10",
- "qry", URLEncoder.encode(qry, CHARSET)
+ "qry", qry
);
JsonNode res = validateJsonResponse(ret);
@@ -2310,7 +2624,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
String ret = content("person", GridRestCommand.EXECUTE_SQL_QUERY,
"type", "Person",
"pageSize", "1",
- "qry", URLEncoder.encode(qry, CHARSET),
+ "qry", qry,
"arg1", "1000",
"arg2", "2000"
);
@@ -2343,7 +2657,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
ret = content("person", GridRestCommand.EXECUTE_SQL_QUERY,
"type", "Person",
"pageSize", "1",
- "qry", URLEncoder.encode(qry, CHARSET),
+ "qry", qry,
"arg1", "1000",
"arg2", "2000"
);
@@ -2506,10 +2820,10 @@ public abstract class
JettyRestProcessorAbstractSelfTest extends JettyRestProces
assertEquals(Time.valueOf("04:04:04"),
cTime.get(Time.valueOf("03:03:03")));
// 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", "2018-02-18 01:01:01", "2017-01-01
02:02:02", STATUS_SUCCESS);
+ putTypedValue("java.sql.timestamp", "2018-01-01 01:01:01", "2018-05-05
05:05:05", STATUS_SUCCESS);
+ putTypedValue("timestamp", "error", "2018-03-18 01:01:01",
STATUS_FAILED);
+ putTypedValue("timestamp", "2018-03-18 01:01:01", "error",
STATUS_FAILED);
putTypedValue("timestamp", "error", "error", STATUS_FAILED);
IgniteCache<Timestamp, Timestamp> cTs = typedCache();
@@ -2664,8 +2978,8 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
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");
+ getTypedValue("Timestamp", "2018-02-18 01:01:01", "test1");
+ getTypedValue("java.sql.timestamp", "2018-01-01 01:01:01", "test2");
// Test UUID type.
IgniteCache<UUID, UUID> cUUID = typedCache();
@@ -2753,6 +3067,12 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
orgCache.put(1, o1);
orgCache.put(2, o2);
+ CacheConfiguration<Integer, Complex> complexCacheCfg = new
CacheConfiguration<>("complex");
+
+ complexCacheCfg.setIndexedTypes(Integer.class, Complex.class);
+
+ grid(0).getOrCreateCache(complexCacheCfg).clear();
+
CacheConfiguration<Integer, Person> personCacheCfg = new
CacheConfiguration<>("person");
personCacheCfg.setIndexedTypes(Integer.class, Person.class);
@@ -2779,6 +3099,87 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
}
/**
+ * @param cacheName Cache name.
+ * @param key Cache key.
+ * @param obj Cache value.
+ * @throws Exception If failed.
+ */
+ private void putObject(String cacheName, String key, Object obj) throws
Exception {
+ putObject(cacheName, key, obj, obj.getClass().getName());
+ }
+
+ /**
+ * @param cacheName Cache name.
+ * @param key Cache key.
+ * @param obj Cache value.
+ * @param type Cache value type.
+ * @throws Exception If failed.
+ */
+ private void putObject(String cacheName, String key, Object obj, String
type) throws Exception {
+ String json = JSON_MAPPER.writeValueAsString(obj);
+
+ info("Put: " + json);
+
+ String ret = content(cacheName, GridRestCommand.CACHE_PUT,
+ "keyType", "int",
+ "key", key,
+ "valueType", type,
+ "val", json
+ );
+
+ info("Put command result: " + ret);
+
+ assertResponseSucceeded(ret, false);
+ }
+
+ /**
+ * @param cacheName Cache name.
+ * @param key Cache key.
+ * @param cls Cache value class.
+ * @param <T> Cache value type.
+ * @return Deserialized object of type {@code T}.
+ * @throws Exception If failed.
+ */
+ private <T> T getObject(String cacheName, String key, Class<T> cls) throws
Exception {
+ String ret = content(cacheName, GridRestCommand.CACHE_GET, "keyType",
"int", "key", key);
+
+ info("Get command result: " + ret);
+
+ JsonNode res = assertResponseSucceeded(ret, false);
+
+ return JSON_MAPPER.treeToValue(res, cls);
+ }
+
+ /**
+ * @param cacheName Cache name.
+ * @param cmd Rest command.
+ * @param params Query parameters.
+ * @return Json query result.
+ * @throws Exception If failed.
+ */
+ private JsonNode queryObject(String cacheName, GridRestCommand cmd,
String... params) throws Exception {
+ String ret = content(cacheName, cmd, params);
+
+ info("SQL command result: " + ret);
+
+ JsonNode res = validateJsonResponse(ret);
+
+ JsonNode items = res.get("items");
+
+ assertNotNull(items);
+
+ assertEquals(1, items.size());
+
+ assertFalse(queryCursorFound());
+
+ JsonNode item = items.get(0);
+
+ JsonNode val = item.get("value");
+
+ return val != null ? val : item;
+ }
+
+ /**
* Organization class.
*/
public static class Organization implements Serializable {
@@ -2878,6 +3279,332 @@ public abstract class
JettyRestProcessorAbstractSelfTest extends JettyRestProces
}
}
+ /** */
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class OuterClass {
+ /** */
+ @JsonProperty
+ private long id;
+
+ /** */
+ @JsonProperty
+ private String name;
+
+ /** */
+ @JsonProperty
+ private double doubleVal;
+
+ /** */
+ @JsonProperty
+ private OptionalObject optional;
+
+ /** */
+ @JsonProperty
+ private ArrayList<Integer> list;
+
+ /** */
+ @JsonProperty
+ private Timestamp timestamp;
+
+ /** */
+ @JsonProperty
+ private long[] longs;
+
+ /** */
+ @JsonProperty
+ private UUID uuid;
+
+ /** */
+ @JsonProperty
+ private IgniteUuid igniteUuid;
+
+ /** */
+ OuterClass() {
+ // No-op.
+ }
+
+ /** */
+ OuterClass(long id, String name, double doubleVal, List<Integer> list)
{
+ this(id, name, doubleVal, list, null, null, null, null, null);
+ }
+
+ /** */
+ @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+ OuterClass(long id, String name, double doubleVal, List<Integer> list,
Timestamp timestamp, long[] longs,
+ UUID uuid, IgniteUuid igniteUuid, OptionalObject optional) {
+ this.id = id;
+ this.name = name;
+ this.doubleVal = doubleVal;
+ this.list = new ArrayList<>(list);
+ this.timestamp = timestamp;
+ this.longs = longs;
+ this.uuid = uuid;
+ this.igniteUuid = igniteUuid;
+ this.optional = optional;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ OuterClass aCls = (OuterClass)o;
+
+ return id == aCls.id &&
+ Double.compare(aCls.doubleVal, doubleVal) == 0 &&
+ Objects.equals(name, aCls.name) &&
+ Objects.equals(optional, aCls.optional) &&
+ Objects.equals(list, aCls.list) &&
+ Objects.equals(timestamp, aCls.timestamp) &&
+ Arrays.equals(longs, aCls.longs) &&
+ Objects.equals(uuid, aCls.uuid) &&
+ Objects.equals(igniteUuid, aCls.igniteUuid);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(id, name, optional, doubleVal, list,
timestamp, longs, uuid, igniteUuid);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "OuterClass [" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", doubleVal=" + doubleVal +
+ ", optional=" + optional +
+ ", list=" + list +
+ ", timestamp=" + timestamp +
+ ", bytes=" + Arrays.toString(longs) +
+ ']';
+ }
+
+ /** */
+ static class OptionalObject {
+ /** String field. */
+ @JsonProperty
+ private String strField;
+
+ /** */
+ OptionalObject() {
+ // No-op.
+ }
+
+ /** */
+ OptionalObject(String strField) {
+ this.strField = strField;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ return (o != null && getClass() == o.getClass()) &&
+ Objects.equals(strField, ((OptionalObject)o).strField);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(strField);
+ }
+ }
+ }
+
+ /** */
+ public enum Color {
+ /** */
+ RED,
+
+ /** */
+ GREEN,
+
+ /** */
+ BLUE
+ }
+
+ /** Complex entity. */
+ @SuppressWarnings({"InnerClassMayBeStatic",
"AssignmentOrReturnOfFieldWithMutableType"})
+ static class Complex implements Serializable {
+ /** */
+ @QuerySqlField(index = true)
+ @JsonProperty
+ private Integer id;
+
+ /** */
+ @QuerySqlField
+ @JsonProperty
+ private String str;
+
+ /** */
+ @QuerySqlField
+ @JsonProperty
+ private Timestamp timestamp;
+
+ /** */
+ @QuerySqlField
+ @JsonProperty
+ private Date sqlDate;
+
+ /** */
+ @JsonProperty
+ private Time time;
+
+ /** */
+ @JsonProperty
+ private java.util.Date date;
+
+ /** */
+ @JsonProperty
+ private HashMap<String, Integer> map;
+
+ /** */
+ @JsonProperty
+ private OuterClass[] objects;
+
+ /** */
+ @JsonProperty
+ private long[] longs;
+
+ /** */
+ @JsonProperty
+ private int[] ints;
+
+ /** */
+ @JsonProperty
+ private byte[] bytes;
+
+ /** */
+ @JsonProperty
+ private char[] chars;
+
+ /** */
+ @JsonProperty
+ private Integer[] integers;
+
+ /** */
+ @JsonProperty
+ private ArrayList<Integer> list;
+
+ /** */
+ @JsonProperty
+ private HashSet<Integer> col;
+
+ /** */
+ @JsonProperty
+ private OuterClass outer;
+
+ /** */
+ @JsonProperty
+ private Color color;
+
+ /** */
+ @JsonProperty
+ private UUID uuid;
+
+ /** */
+ @JsonProperty
+ private IgniteUuid igniteUuid;
+
+ /** */
+ Complex() {
+ // No-op.
+ }
+
+ /** */
+ Complex(Integer id, String str, Timestamp timestamp, Date sqlDate,
Time time, java.util.Date date,
+ Map<String, Integer> map, int[] ints, Integer[] integers, long[]
longs, List<Integer> list,
+ Set<Integer> col, byte[] bytes, char[] chars, Color color,
OuterClass outer, OuterClass[] objects,
+ UUID uuid, IgniteUuid igniteUuid) {
+ this.id = id;
+ this.str = str;
+ this.timestamp = timestamp;
+ this.sqlDate = sqlDate;
+ this.time = time;
+ this.date = date;
+ this.map = new HashMap<>(map);
+ this.ints = ints;
+ this.longs = longs;
+ this.list = new ArrayList<>(list);
+ this.integers = integers;
+ this.col = new HashSet<>(col);
+ this.bytes = bytes;
+ this.chars = chars;
+ this.color = color;
+ this.outer = outer;
+ this.objects = objects;
+ this.uuid = uuid;
+ this.igniteUuid = igniteUuid;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ Complex complex = (Complex)o;
+
+ return Objects.equals(id, complex.id) &&
+ Objects.equals(str, complex.str) &&
+ Objects.equals(timestamp, complex.timestamp) &&
+ Objects.equals(sqlDate, complex.sqlDate) &&
+ Objects.equals(time, complex.time) &&
+ Objects.equals(date, complex.date) &&
+ Objects.equals(map, complex.map) &&
+ Arrays.equals(longs, complex.longs) &&
+ Arrays.equals(ints, complex.ints) &&
+ Arrays.equals(bytes, complex.bytes) &&
+ Arrays.equals(chars, complex.chars) &&
+ Arrays.equals(integers, complex.integers) &&
+ Objects.equals(list, complex.list) &&
+ Objects.equals(col, complex.col) &&
+ Objects.equals(outer, complex.outer) &&
+ Arrays.equals(objects, complex.objects) &&
+ Objects.equals(uuid, complex.uuid) &&
+ Objects.equals(igniteUuid, complex.igniteUuid) &&
+ color == complex.color;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int result = Objects.hash(id, str, timestamp, sqlDate, time, date,
+ map, list, col, outer, color, objects, uuid, igniteUuid);
+
+ result = 31 * result + Arrays.hashCode(longs);
+ result = 31 * result + Arrays.hashCode(ints);
+ result = 31 * result + Arrays.hashCode(bytes);
+ result = 31 * result + Arrays.hashCode(chars);
+ result = 31 * result + Arrays.hashCode(integers);
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "Complex [" +
+ "id=" + id +
+ ", string='" + str + '\'' +
+ ", timestamp=" + timestamp +
+ ", sqlDate=" + sqlDate +
+ ", time=" + time +
+ ", date=" + date +
+ ", map=" + map +
+ ", longs=" + Arrays.toString(longs) +
+ ", ints=" + Arrays.toString(ints) +
+ ", bytes=" + Arrays.toString(bytes) +
+ ", chars=" + Arrays.toString(chars) +
+ ", integers=" + Arrays.toString(integers) +
+ ", list=" + list +
+ ", col=" + col +
+ ", outer=" + outer +
+ ", color=" + color +
+ ']';
+ }
+ }
+
/**
* Person class.
*/
@@ -2891,6 +3618,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
/** Organization id. */
@QuerySqlField(index = true)
+ @JsonProperty("orgId")
private Integer orgId;
/** First name (not-indexed). */
@@ -2905,6 +3633,11 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
@QuerySqlField(index = true)
private double salary;
+ /** */
+ Person() {
+ // No-op.
+ }
+
/**
* @param firstName First name.
* @param lastName Last name.
@@ -3134,7 +3867,7 @@ public abstract class JettyRestProcessorAbstractSelfTest
extends JettyRestProces
first = false;
}
- put("p" + idx++, URLEncoder.encode(sb.toString(), CHARSET));
+ put("p" + idx++, sb.toString());
return this;
}
diff --git
a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java
b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java
index 5f14e2c..ee5c2c4 100644
---
a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java
+++
b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java
@@ -17,18 +17,18 @@
package org.apache.ignite.internal.processors.rest;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import
org.apache.ignite.internal.processors.rest.protocols.http.jetty.GridJettyObjectMapper;
import org.apache.ignite.internal.util.typedef.internal.SB;
@@ -38,6 +38,9 @@ import static
org.apache.ignite.IgniteSystemProperties.IGNITE_JETTY_PORT;
* Base class for testing Jetty REST protocol.
*/
public abstract class JettyRestProcessorCommonSelfTest extends
AbstractRestProcessorSelfTest {
+ /** Used to encode request parameters. */
+ private static final String CHARSET = StandardCharsets.UTF_8.name();
+
/** Grid count. */
private static final int GRID_CNT = 3;
@@ -104,7 +107,7 @@ public abstract class JettyRestProcessorCommonSelfTest
extends AbstractRestProce
SB sb = new SB(restUrl());
for (Map.Entry<String, String> e : params.entrySet())
- sb.a(e.getKey()).a('=').a(e.getValue()).a('&');
+ sb.a(e.getKey()).a('=').a(URLEncoder.encode(e.getValue(),
CHARSET)).a('&');
URL url = new URL(sb.toString());
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 4b71aa2..a75817e 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -2950,6 +2950,17 @@ public class GridQueryProcessor extends
GridProcessorAdapter {
}
/**
+ * Gets type descriptor for provided cache name and type name if type is
still valid.
+ *
+ * @param cacheName Cache name.
+ * @param typeName Type name.
+ * @return Query type descriptor or {@code null} if descriptor was not
found.
+ */
+ public @Nullable GridQueryTypeDescriptor typeDescriptor(@Nullable String
cacheName, String typeName) {
+ return typesByName.get(new QueryTypeNameKey(cacheName, typeName));
+ }
+
+ /**
* @param qryType Query type.
* @param qry Query description.
* @param cctx Cache context.
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
index 21f8ca3..b615540 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/query/QueryCommandHandler.java
@@ -322,6 +322,9 @@ public class QueryCommandHandler extends
GridRestCommandHandlerAdapter {
return new GridRestResponse(GridRestResponse.STATUS_FAILED,
"Failed to find cache with name: " + cacheName);
+ if (req.keepBinary())
+ cache = cache.withKeepBinary();
+
final QueryCursor qryCur = cache.query(qry);
Iterator cur = qryCur.iterator();
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
index ca24a27..0263e04 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/RestQueryRequest.java
@@ -48,6 +48,9 @@ public class RestQueryRequest extends GridRestRequest {
/** Query type. */
private QueryType type;
+ /** Keep binary flag. */
+ private boolean keepBinary;
+
/**
* @param sqlQry Sql query.
*/
@@ -161,6 +164,20 @@ public class RestQueryRequest extends GridRestRequest {
}
/**
+ * @return Keep binary flag.
+ */
+ public boolean keepBinary() {
+ return keepBinary;
+ }
+
+ /**
+ * @param keepBinary Keep binary flag.
+ */
+ public void keepBinary(boolean keepBinary) {
+ this.keepBinary = keepBinary;
+ }
+
+ /**
* @param type Query type.
*/
public void queryType(QueryType type) {
diff --git
a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java
b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java
index 92ee5c0..28c4298 100644
---
a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java
+++
b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java
@@ -17,14 +17,14 @@
package org.apache.ignite.internal.processors.rest.protocols.http.jetty;
-import java.io.IOException;
-import java.sql.SQLException;
-import java.text.DateFormat;
-import java.util.Locale;
-
import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
@@ -33,9 +33,17 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
-
+import java.io.IOException;
+import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.util.Locale;
+import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectException;
import org.apache.ignite.binary.BinaryType;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.binary.BinaryEnumObjectImpl;
import org.apache.ignite.internal.binary.BinaryObjectImpl;
import
org.apache.ignite.internal.processors.cache.query.GridCacheSqlIndexMetadata;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata;
@@ -52,6 +60,13 @@ public class GridJettyObjectMapper extends ObjectMapper {
* Default constructor.
*/
public GridJettyObjectMapper() {
+ this(null);
+ }
+
+ /**
+ * @param ctx Defines a kernal context to enable deserialization into the
Ignite binary object.
+ */
+ GridJettyObjectMapper(GridKernalContext ctx) {
super(null, new CustomSerializerProvider(), null);
setDateFormat(DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
DateFormat.DEFAULT, Locale.US));
@@ -65,7 +80,19 @@ public class GridJettyObjectMapper extends ObjectMapper {
module.addSerializer(GridCacheSqlIndexMetadata.class,
IGNITE_SQL_INDEX_METADATA_SERIALIZER);
module.addSerializer(BinaryObjectImpl.class,
IGNITE_BINARY_OBJECT_SERIALIZER);
+ // Standard serializer loses nanoseconds.
+ module.addSerializer(Timestamp.class, IGNITE_TIMESTAMP_SERIALIZER);
+ module.addDeserializer(Timestamp.class, IGNITE_TIMESTAMP_DESERIALIZER);
+
+ // Standard serializer may incorrectly apply timezone.
+ module.addSerializer(Date.class, IGNITE_SQLDATE_SERIALIZER);
+ module.addDeserializer(Date.class, IGNITE_SQLDATE_DESERIALIZER);
+
+ if (ctx != null)
+ module.addDeserializer(BinaryObject.class, new
IgniteBinaryObjectJsonDeserializer(ctx));
+
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
registerModule(module);
}
@@ -247,7 +274,10 @@ public class GridJettyObjectMapper extends ObjectMapper {
throw ser.mappingException("Failed convert to
JSON object for circular references");
}
- gen.writeObjectField(name, val);
+ if (val instanceof BinaryEnumObjectImpl)
+ gen.writeObjectField(name,
((BinaryObject)val).enumName());
+ else
+ gen.writeObjectField(name, val);
}
gen.writeEndObject();
@@ -264,4 +294,39 @@ public class GridJettyObjectMapper extends ObjectMapper {
}
}
};
+
+ /** Custom serializer for {@link java.sql.Timestamp}. */
+ private static final JsonSerializer<Timestamp> IGNITE_TIMESTAMP_SERIALIZER
= new JsonSerializer<Timestamp>() {
+ /** {@inheritDoc} */
+ @Override public void serialize(Timestamp timestamp, JsonGenerator gen,
+ SerializerProvider provider) throws IOException {
+ gen.writeString(timestamp.toString());
+ }
+ };
+
+ /** Custom serializer for {@link java.sql.Date}. */
+ private static final JsonSerializer<Date> IGNITE_SQLDATE_SERIALIZER = new
JsonSerializer<Date>() {
+ /** {@inheritDoc} */
+ @Override public void serialize(Date date, JsonGenerator gen,
SerializerProvider prov) throws IOException {
+ gen.writeString(date.toString());
+ }
+ };
+
+ /** Custom deserializer for {@link java.sql.Timestamp}. */
+ private static final JsonDeserializer<Timestamp>
IGNITE_TIMESTAMP_DESERIALIZER = new JsonDeserializer<Timestamp>() {
+ /** {@inheritDoc} */
+ @Override public Timestamp deserialize(JsonParser parser,
+ DeserializationContext ctx) throws IOException,
JsonProcessingException {
+ return Timestamp.valueOf(parser.getText());
+ }
+ };
+
+ /** Custom deserializer for {@link java.sql.Date}. */
+ private static final JsonDeserializer<Date> IGNITE_SQLDATE_DESERIALIZER =
new JsonDeserializer<Date>() {
+ /** {@inheritDoc} */
+ @Override public Date deserialize(JsonParser parser,
+ DeserializationContext ctx) throws IOException,
JsonProcessingException {
+ return Date.valueOf(parser.getText());
+ }
+ };
}
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 35cd4e0..f01f3de 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
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.processors.rest.protocols.http.jetty;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -46,8 +47,10 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.cluster.ClusterState;
+import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.cache.CacheConfigurationOverride;
import org.apache.ignite.internal.processors.rest.GridRestCommand;
import org.apache.ignite.internal.processors.rest.GridRestProtocolHandler;
@@ -64,6 +67,7 @@ import
org.apache.ignite.internal.processors.rest.request.GridRestTaskRequest;
import
org.apache.ignite.internal.processors.rest.request.GridRestTopologyRequest;
import org.apache.ignite.internal.processors.rest.request.RestQueryRequest;
import
org.apache.ignite.internal.processors.rest.request.RestUserActionRequest;
+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.lang.IgniteBiTuple;
@@ -157,16 +161,16 @@ public class GridJettyRestHandler extends AbstractHandler
{
*
* @param hnd Handler.
* @param authChecker Authentication checking closure.
- * @param log Logger.
+ * @param ctx Kernal context.
*/
- GridJettyRestHandler(GridRestProtocolHandler hnd, IgniteClosure<String,
Boolean> authChecker, IgniteLogger log) {
+ GridJettyRestHandler(GridRestProtocolHandler hnd, C1<String, Boolean>
authChecker, GridKernalContext ctx) {
assert hnd != null;
- assert log != null;
+ assert ctx != null;
this.hnd = hnd;
- this.log = log;
this.authChecker = authChecker;
- this.jsonMapper = new GridJettyObjectMapper();
+ this.log = ctx.log(getClass());
+ this.jsonMapper = new GridJettyObjectMapper(ctx);
// Init default page and favicon.
try {
@@ -199,11 +203,11 @@ public class GridJettyRestHandler extends AbstractHandler
{
* @return Long value from parameters map or {@code dfltVal} if null or
not exists.
* @throws IgniteCheckedException If parsing failed.
*/
- @Nullable private static Long longValue(String key, Map<String, Object>
params,
+ @Nullable private static Long longValue(String key, Map<String, String>
params,
Long dfltVal) throws IgniteCheckedException {
assert key != null;
- String val = (String)params.get(key);
+ String val = params.get(key);
try {
return val == null ? dfltVal : Long.valueOf(val);
@@ -221,10 +225,10 @@ public class GridJettyRestHandler extends AbstractHandler
{
* @param dfltVal Default value.
* @return Boolean value from parameters map or {@code dfltVal} if null or
not exists.
*/
- private static boolean booleanValue(String key, Map<String, Object>
params, boolean dfltVal) {
+ private static boolean booleanValue(String key, Map<String, String>
params, boolean dfltVal) {
assert key != null;
- String val = (String)params.get(key);
+ String val = params.get(key);
return val == null ? dfltVal : Boolean.parseBoolean(val);
}
@@ -238,10 +242,10 @@ public class GridJettyRestHandler extends AbstractHandler
{
* @return Integer value from parameters map or {@code dfltVal} if null or
not exists.
* @throws IgniteCheckedException If parsing failed.
*/
- private static int intValue(String key, Map<String, Object> params, int
dfltVal) throws IgniteCheckedException {
+ private static int intValue(String key, Map<String, String> params, int
dfltVal) throws IgniteCheckedException {
assert key != null;
- String val = (String)params.get(key);
+ String val = params.get(key);
try {
return val == null ? dfltVal : Integer.parseInt(val);
@@ -253,13 +257,13 @@ public class GridJettyRestHandler extends AbstractHandler
{
private static <T extends Enum<T>> @Nullable T enumValue(
String key,
- Map<String, Object> params,
+ Map<String, String> params,
Class<T> enumClass
) throws IgniteCheckedException {
assert key != null;
assert enumClass != null;
- String val = (String)params.get(key);
+ String val = params.get(key);
if (val == null)
return null;
@@ -280,10 +284,10 @@ public class GridJettyRestHandler extends AbstractHandler
{
* @return UUID value from parameters map or {@code null} if null or not
exists.
* @throws IgniteCheckedException If parsing failed.
*/
- @Nullable private static UUID uuidValue(String key, Map<String, Object>
params) throws IgniteCheckedException {
+ @Nullable private static UUID uuidValue(String key, Map<String, String>
params) throws IgniteCheckedException {
assert key != null;
- String val = (String)params.get(key);
+ String val = params.get(key);
try {
return val == null ? null : UUID.fromString(val);
@@ -431,7 +435,7 @@ public class GridJettyRestHandler extends AbstractHandler {
GridRestResponse cmdRes;
- Map<String, Object> params = parameters(req);
+ Map<String, String> params = parameters(req);
try {
GridRestRequest cmdReq = createRequest(cmd, params, req);
@@ -493,81 +497,6 @@ 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.
@@ -578,7 +507,7 @@ public class GridJettyRestHandler extends AbstractHandler {
*/
@Nullable private GridRestRequest createRequest(
GridRestCommand cmd,
- Map<String, Object> params,
+ Map<String, String> params,
HttpServletRequest req
) throws IgniteCheckedException {
GridRestRequest restReq;
@@ -587,14 +516,14 @@ public class GridJettyRestHandler extends AbstractHandler
{
case GET_OR_CREATE_CACHE: {
GridRestCacheRequest restReq0 = new GridRestCacheRequest();
- restReq0.cacheName((String)params.get(CACHE_NAME_PARAM));
+ restReq0.cacheName(params.get(CACHE_NAME_PARAM));
- String templateName = (String)params.get(TEMPLATE_NAME_PARAM);
+ String templateName = params.get(TEMPLATE_NAME_PARAM);
if (!F.isEmpty(templateName))
restReq0.templateName(templateName);
- String backups = (String)params.get(BACKUPS_PARAM);
+ String backups = params.get(BACKUPS_PARAM);
CacheConfigurationOverride cfg = new
CacheConfigurationOverride();
@@ -609,19 +538,19 @@ public class GridJettyRestHandler extends AbstractHandler
{
}
// Set cache group name.
- String cacheGrp = (String)params.get(CACHE_GROUP_PARAM);
+ String cacheGrp = params.get(CACHE_GROUP_PARAM);
if (!F.isEmpty(cacheGrp))
cfg.cacheGroup(cacheGrp);
// Set cache data region name.
- String dataRegion = (String)params.get(DATA_REGION_PARAM);
+ String dataRegion = params.get(DATA_REGION_PARAM);
if (!F.isEmpty(dataRegion))
cfg.dataRegion(dataRegion);
// Set cache write mode.
- String wrtSyncMode =
(String)params.get(WRITE_SYNCHRONIZATION_MODE_PARAM);
+ String wrtSyncMode =
params.get(WRITE_SYNCHRONIZATION_MODE_PARAM);
if (!F.isEmpty(wrtSyncMode)) {
try {
@@ -643,7 +572,7 @@ public class GridJettyRestHandler extends AbstractHandler {
case DESTROY_CACHE: {
GridRestCacheRequest restReq0 = new GridRestCacheRequest();
- restReq0.cacheName((String)params.get(CACHE_NAME_PARAM));
+ restReq0.cacheName(params.get(CACHE_NAME_PARAM));
restReq = restReq0;
@@ -690,17 +619,19 @@ public class GridJettyRestHandler extends AbstractHandler
{
case CACHE_PREPEND: {
GridRestCacheRequest restReq0 = new GridRestCacheRequest();
- String cacheName = (String)params.get(CACHE_NAME_PARAM);
+ String cacheName = params.get(CACHE_NAME_PARAM);
restReq0.cacheName(F.isEmpty(cacheName) ? null : cacheName);
- String keyType = (String)params.get("keyType");
- String valType = (String)params.get("valueType");
+ String keyType = params.get("keyType");
+ String valType = params.get("valueType");
+
+ Converter converter = new Converter(cacheName);
- restReq0.key(convert(keyType, params.get("key")));
- restReq0.value(convert(valType, params.get("val")));
- restReq0.value2(convert(valType, params.get("val2")));
+ restReq0.key(converter.convert(keyType, params.get("key")));
+ restReq0.value(converter.convert(valType, params.get("val")));
+ restReq0.value2(converter.convert(valType,
params.get("val2")));
- Object val1 = convert(valType, params.get("val1"));
+ Object val1 = converter.convert(valType, params.get("val1"));
if (val1 != null)
restReq0.value(val1);
@@ -711,8 +642,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(keyType, "k", params);
- List<Object> vals = values(valType, "v", params);
+ List<Object> keys = converter.values(keyType, "k", params);
+ List<Object> vals = converter.values(valType, "v", params);
if (keys.size() < vals.size())
throw new IgniteCheckedException("Number of keys must
be greater or equals to number of values.");
@@ -737,13 +668,13 @@ public class GridJettyRestHandler extends AbstractHandler
{
case NODE: {
GridRestTopologyRequest restReq0 = new
GridRestTopologyRequest();
-
restReq0.includeMetrics(Boolean.parseBoolean((String)params.get("mtr")));
-
restReq0.includeAttributes(Boolean.parseBoolean((String)params.get("attr")));
+
restReq0.includeMetrics(Boolean.parseBoolean(params.get("mtr")));
+
restReq0.includeAttributes(Boolean.parseBoolean(params.get("attr")));
- String caches = (String)params.get("caches");
+ String caches = params.get("caches");
restReq0.includeCaches(caches == null ||
Boolean.parseBoolean(caches));
- restReq0.nodeIp((String)params.get("ip"));
+ restReq0.nodeIp(params.get("ip"));
restReq0.nodeId(uuidValue("id", params));
@@ -757,12 +688,12 @@ public class GridJettyRestHandler extends AbstractHandler
{
case NOOP: {
GridRestTaskRequest restReq0 = new GridRestTaskRequest();
- restReq0.taskId((String)params.get("id"));
- restReq0.taskName((String)params.get("name"));
+ restReq0.taskId(params.get("id"));
+ restReq0.taskName(params.get("name"));
- restReq0.params(values(null, "p", params));
+ restReq0.params(new Converter().values(null, "p", params));
-
restReq0.async(Boolean.parseBoolean((String)params.get("async")));
+ restReq0.async(Boolean.parseBoolean(params.get("async")));
restReq0.timeout(longValue("timeout", params, 0L));
@@ -774,7 +705,7 @@ public class GridJettyRestHandler extends AbstractHandler {
case LOG: {
GridRestLogRequest restReq0 = new GridRestLogRequest();
- restReq0.path((String)params.get("path"));
+ restReq0.path(params.get("path"));
restReq0.from(intValue("from", params, -1));
restReq0.to(intValue("to", params, -1));
@@ -846,7 +777,7 @@ public class GridJettyRestHandler extends AbstractHandler {
GridRestBaselineRequest restReq0 = new
GridRestBaselineRequest();
restReq0.topologyVersion(longValue("topVer", params, null));
- restReq0.consistentIds(values(null, "consistentId", params));
+ restReq0.consistentIds(new Converter().values(null,
"consistentId", params));
restReq = restReq0;
@@ -864,8 +795,8 @@ public class GridJettyRestHandler extends AbstractHandler {
case UPDATE_USER: {
RestUserActionRequest restReq0 = new RestUserActionRequest();
- restReq0.user((String)params.get("user"));
- restReq0.password((String)params.get("password"));
+ restReq0.user(params.get("user"));
+ restReq0.password(params.get("password"));
restReq = restReq0;
@@ -876,23 +807,30 @@ public class GridJettyRestHandler extends AbstractHandler
{
case EXECUTE_SQL_FIELDS_QUERY: {
RestQueryRequest restReq0 = new RestQueryRequest();
- restReq0.sqlQuery((String)params.get("qry"));
+ String cacheName = params.get(CACHE_NAME_PARAM);
- restReq0.arguments(values(null, "arg", params).toArray());
+ restReq0.sqlQuery(params.get("qry"));
- restReq0.typeName((String)params.get("type"));
+ restReq0.arguments(new Converter(cacheName).values(null,
"arg", params).toArray());
- String pageSize = (String)params.get("pageSize");
+ restReq0.typeName(params.get("type"));
+
+ String pageSize = params.get("pageSize");
if (pageSize != null)
restReq0.pageSize(Integer.parseInt(pageSize));
- String distributedJoins =
(String)params.get("distributedJoins");
+ String keepBinary = params.get("keepBinary");
+
+ if (keepBinary != null)
+ restReq0.keepBinary(Boolean.parseBoolean(keepBinary));
+
+ String distributedJoins = params.get("distributedJoins");
if (distributedJoins != null)
restReq0.distributedJoins(Boolean.parseBoolean(distributedJoins));
- restReq0.cacheName((String)params.get(CACHE_NAME_PARAM));
+ restReq0.cacheName(cacheName);
if (cmd == EXECUTE_SQL_QUERY)
restReq0.queryType(RestQueryRequest.QueryType.SQL);
@@ -907,16 +845,16 @@ public class GridJettyRestHandler extends AbstractHandler
{
case EXECUTE_SCAN_QUERY: {
RestQueryRequest restReq0 = new RestQueryRequest();
- restReq0.sqlQuery((String)params.get("qry"));
+ restReq0.sqlQuery(params.get("qry"));
- String pageSize = (String)params.get("pageSize");
+ String pageSize = params.get("pageSize");
if (pageSize != null)
restReq0.pageSize(Integer.parseInt(pageSize));
- restReq0.cacheName((String)params.get(CACHE_NAME_PARAM));
+ restReq0.cacheName(params.get(CACHE_NAME_PARAM));
- restReq0.className((String)params.get("className"));
+ restReq0.className(params.get("className"));
restReq0.queryType(RestQueryRequest.QueryType.SCAN);
@@ -928,17 +866,17 @@ public class GridJettyRestHandler extends AbstractHandler
{
case FETCH_SQL_QUERY: {
RestQueryRequest restReq0 = new RestQueryRequest();
- String qryId = (String)params.get("qryId");
+ String qryId = params.get("qryId");
if (qryId != null)
restReq0.queryId(Long.parseLong(qryId));
- String pageSize = (String)params.get("pageSize");
+ String pageSize = params.get("pageSize");
if (pageSize != null)
restReq0.pageSize(Integer.parseInt(pageSize));
- restReq0.cacheName((String)params.get(CACHE_NAME_PARAM));
+ restReq0.cacheName(params.get(CACHE_NAME_PARAM));
restReq = restReq0;
@@ -948,12 +886,12 @@ public class GridJettyRestHandler extends AbstractHandler
{
case CLOSE_SQL_QUERY: {
RestQueryRequest restReq0 = new RestQueryRequest();
- String qryId = (String)params.get("qryId");
+ String qryId = params.get("qryId");
if (qryId != null)
restReq0.queryId(Long.parseLong(qryId));
- restReq0.cacheName((String)params.get(CACHE_NAME_PARAM));
+ restReq0.cacheName(params.get(CACHE_NAME_PARAM));
restReq = restReq0;
@@ -977,7 +915,7 @@ public class GridJettyRestHandler extends AbstractHandler {
if (!credentials(params, IGNITE_LOGIN, IGNITE_PASSWORD, restReq))
credentials(params, USER_PARAM, PWD_PARAM, restReq);
- String clientId = (String)params.get("clientId");
+ String clientId = params.get("clientId");
try {
if (clientId != null)
@@ -987,7 +925,7 @@ public class GridJettyRestHandler extends AbstractHandler {
// Ignore invalid client id. Rest handler will process this logic.
}
- String destId = (String)params.get("destId");
+ String destId = params.get("destId");
try {
if (destId != null)
@@ -997,7 +935,7 @@ public class GridJettyRestHandler extends AbstractHandler {
// Don't fail - try to execute locally.
}
- String sesTokStr = (String)params.get("sessionToken");
+ String sesTokStr = params.get("sessionToken");
try {
if (sesTokStr != null) {
@@ -1022,13 +960,12 @@ public class GridJettyRestHandler extends
AbstractHandler {
* @param restReq Request to add credentials if any.
* @return {@code true} If params contains credentials.
*/
- private boolean credentials(Map<String, Object> params, String userParam,
String pwdParam,
+ private boolean credentials(Map<String, String> params, String userParam,
String pwdParam,
GridRestRequest restReq) {
boolean hasCreds = params.containsKey(userParam) ||
params.containsKey(pwdParam);
if (hasCreds) {
- SecurityCredentials cred = new
SecurityCredentials((String)params.get(userParam),
- (String)params.get(pwdParam));
+ SecurityCredentials cred = new
SecurityCredentials(params.get(userParam), params.get(pwdParam));
restReq.credentials(cred);
}
@@ -1037,32 +974,6 @@ 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 type, String keyPrefix,
- Map<String, Object> params) throws IgniteCheckedException {
- assert keyPrefix != null;
-
- List<Object> vals = new LinkedList<>();
-
- for (int i = 1; ; i++) {
- String key = keyPrefix + i;
-
- if (params.containsKey(key))
- vals.add(convert(type, params.get(key)));
- else
- break;
- }
-
- return vals;
- }
-
- /**
* @param req Request.
* @return Command.
*/
@@ -1078,13 +989,13 @@ public class GridJettyRestHandler extends
AbstractHandler {
* @param req Request.
* @return Map of parsed parameters.
*/
- private Map<String, Object> parameters(ServletRequest req) {
+ private Map<String, String> parameters(ServletRequest req) {
Map<String, String[]> params = req.getParameterMap();
if (F.isEmpty(params))
return Collections.emptyMap();
- Map<String, Object> map = U.newHashMap(params.size());
+ Map<String, String> map = U.newHashMap(params.size());
for (Map.Entry<String, String[]> entry : params.entrySet())
map.put(entry.getKey(), parameter(entry.getValue()));
@@ -1107,6 +1018,138 @@ public class GridJettyRestHandler extends
AbstractHandler {
}
/**
+ * Converter from string into specified type.
+ */
+ private class Converter {
+ /** Cache name. */
+ private final String cacheName;
+
+ /**
+ * @param cacheName Cache name.
+ */
+ private Converter(String cacheName) {
+ this.cacheName = cacheName;
+ }
+
+ /** */
+ private Converter() {
+ this(null);
+ }
+
+ /**
+ * Gets and converts 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.
+ * @throws IgniteCheckedException If failed to convert.
+ */
+ private List<Object> values(String type, String keyPrefix,
+ Map<String, String> params) throws IgniteCheckedException {
+ assert keyPrefix != null;
+
+ List<Object> vals = new LinkedList<>();
+
+ for (int i = 1; ; i++) {
+ String key = keyPrefix + i;
+
+ if (params.containsKey(key))
+ vals.add(convert(type, params.get(key)));
+ else
+ break;
+ }
+
+ return vals;
+ }
+
+ /**
+ * @param type Optional value type.
+ * @param str String to convert.
+ * @return Converted value.
+ * @throws IgniteCheckedException If failed to convert.
+ */
+ private Object convert(@Nullable String type, @Nullable String str)
throws IgniteCheckedException {
+ if (F.isEmpty(type) || str == null)
+ return str;
+
+ try {
+ switch (type.toLowerCase()) {
+ case "boolean":
+ case "java.lang.boolean":
+ return Boolean.valueOf(str);
+
+ case "byte":
+ case "java.lang.byte":
+ return Byte.valueOf(str);
+
+ case "short":
+ case "java.lang.short":
+ return Short.valueOf(str);
+
+ case "int":
+ case "integer":
+ case "java.lang.integer":
+ return Integer.valueOf(str);
+
+ case "long":
+ case "java.lang.long":
+ return Long.valueOf(str);
+
+ case "float":
+ case "java.lang.float":
+ return Float.valueOf(str);
+
+ case "double":
+ case "java.lang.double":
+ return Double.valueOf(str);
+
+ case "date":
+ case "java.sql.date":
+ return Date.valueOf(str);
+
+ case "time":
+ case "java.sql.time":
+ return Time.valueOf(str);
+
+ case "timestamp":
+ case "java.sql.timestamp":
+ return Timestamp.valueOf(str);
+
+ case "uuid":
+ case "java.util.uuid":
+ return UUID.fromString(str);
+
+ case "igniteuuid":
+ case "org.apache.ignite.lang.igniteuuid":
+ return IgniteUuid.fromString(str);
+
+ case "string":
+ case "java.lang.string":
+ return str;
+ }
+
+ // Creating an object of the specified type, if its class is
available.
+ Class<?> cls = U.classForName(type, null);
+
+ if (cls != null)
+ return jsonMapper.readValue(str, cls);
+
+ // Creating a binary object if the type is not a class name or
it cannot be loaded.
+ InjectableValues.Std prop = new InjectableValues.Std()
+
.addValue(IgniteBinaryObjectJsonDeserializer.BINARY_TYPE_PROPERTY, type)
+
.addValue(IgniteBinaryObjectJsonDeserializer.CACHE_NAME_PROPERTY, cacheName);
+
+ return
jsonMapper.reader(prop).forType(BinaryObject.class).readValue(str);
+ }
+ catch (Throwable e) {
+ throw new IgniteCheckedException("Failed to convert value to
specified type [type=" + type +
+ ", val=" + str + ", reason=" + e.getClass().getName() + ":
" + e.getMessage() + "]", e);
+ }
+ }
+ }
+
+ /**
* Special stream to check JSON serialization.
*/
private static class NullOutputStream extends OutputStream {
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 76eeea2..60134a8 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
@@ -129,7 +129,7 @@ public class GridJettyRestProtocol extends
GridRestProtocolAdapter {
@Override public Boolean apply(String tok) {
return F.isEmpty(secretKey) || authenticate(tok);
}
- }, log);
+ }, ctx);
String jettyPath = config().getJettyPath();
diff --git
a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/IgniteBinaryObjectJsonDeserializer.java
b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/IgniteBinaryObjectJsonDeserializer.java
new file mode 100644
index 0000000..f156fe4
--- /dev/null
+++
b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/IgniteBinaryObjectJsonDeserializer.java
@@ -0,0 +1,123 @@
+/*
+ * 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.processors.rest.protocols.http.jetty;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.binary.BinaryObjectBuilder;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.binary.BinaryFieldMetadata;
+import org.apache.ignite.internal.binary.BinaryTypeImpl;
+import org.apache.ignite.internal.binary.BinaryUtils;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+
+/**
+ * JSON deserializer into the Ignite binary object.
+ */
+public class IgniteBinaryObjectJsonDeserializer extends
JsonDeserializer<BinaryObject> {
+ /** Property name to set binary type name. */
+ public static final String BINARY_TYPE_PROPERTY = "binaryTypeName";
+
+ /** Property name to set cache name. */
+ public static final String CACHE_NAME_PROPERTY = "cacheName";
+
+ /** Kernal context. */
+ private final GridKernalContext ctx;
+
+ /**
+ * @param ctx Kernal context.
+ */
+ public IgniteBinaryObjectJsonDeserializer(GridKernalContext ctx) {
+ assert ctx != null;
+
+ this.ctx = ctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public BinaryObject deserialize(JsonParser parser,
DeserializationContext dCtx) throws IOException {
+ String type = (String)dCtx.findInjectableValue(BINARY_TYPE_PROPERTY,
null, null);
+ String cacheName =
(String)dCtx.findInjectableValue(CACHE_NAME_PROPERTY, null, null);
+
+ assert type != null;
+
+ JsonNode tree = parser.readValueAsTree();
+ ObjectCodec mapper = parser.getCodec();
+
+ Map<String, BinaryFieldMetadata> binFields = binaryFields(type);
+ Map<String, Class<?>> qryFields = queryFields(cacheName, type);
+
+ BinaryObjectBuilder builder = ctx.cacheObjects().builder(type);
+ Iterator<Map.Entry<String, JsonNode>> itr = tree.fields();
+
+ while (itr.hasNext()) {
+ Map.Entry<String, JsonNode> entry = itr.next();
+
+ String field = entry.getKey();
+ JsonNode node = entry.getValue();
+
+ BinaryFieldMetadata meta = binFields.get(field);
+
+ Class<?> fieldCls = meta != null ?
BinaryUtils.FLAG_TO_CLASS.get((byte)meta.typeId()) : null;
+
+ if (fieldCls == null)
+ fieldCls =
qryFields.getOrDefault(QueryUtils.normalizeObjectName(field, true),
Object.class);
+
+ builder.setField(field, mapper.treeToValue(node, fieldCls));
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * @param cacheName Cache name.
+ * @param type Type name.
+ * @return Mapping from field name to its type.
+ */
+ private Map<String, Class<?>> queryFields(String cacheName, String type) {
+ if (ctx.query().moduleEnabled()) {
+ GridQueryTypeDescriptor desc =
ctx.query().typeDescriptor(cacheName, type);
+
+ if (desc != null)
+ return desc.fields();
+ }
+
+ return Collections.emptyMap();
+ }
+
+ /**
+ * @param type Binary type name.
+ * @return Mapping from field name to its binary type.
+ */
+ private Map<String, BinaryFieldMetadata> binaryFields(String type) {
+ BinaryTypeImpl binType =
(BinaryTypeImpl)ctx.cacheObjects().binary().type(type);
+
+ if (binType != null)
+ return binType.metadata().fieldsMap();
+
+ return Collections.emptyMap();
+ }
+}