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();
+    }
+}

Reply via email to