IGNITE-6244 .NET: Thin client: ScanQuery

This closes #2693


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

Branch: refs/heads/master
Commit: 86372dee2e3266667aab258dcfe2b0e062dbebbe
Parents: 879fffa
Author: Pavel Tupitsyn <ptupit...@apache.org>
Authored: Tue Sep 19 12:09:18 2017 +0300
Committer: Pavel Tupitsyn <ptupit...@apache.org>
Committed: Tue Sep 19 12:09:18 2017 +0300

----------------------------------------------------------------------
 .../odbc/ClientListenerNioListener.java         |   2 +-
 .../odbc/jdbc/JdbcRequestHandler.java           |   7 +-
 .../odbc/odbc/OdbcRequestHandler.java           |   7 +-
 .../query/PlatformAbstractQueryCursor.java      |   9 +-
 .../platform/client/ClientBooleanResponse.java  |   4 +-
 .../client/ClientCloseableResource.java         |  28 +++
 .../client/ClientConnectionContext.java         |  65 ++++-
 .../platform/client/ClientMessageParser.java    |  26 +-
 .../platform/client/ClientObjectResponse.java   |   4 +-
 .../platform/client/ClientRequest.java          |   3 +-
 .../platform/client/ClientRequestHandler.java   |  14 +-
 .../client/ClientResourceCloseRequest.java      |  46 ++++
 .../platform/client/ClientResourceRegistry.java |  98 ++++++++
 .../platform/client/ClientResponse.java         |  22 +-
 .../platform/client/ClientStringResponse.java   |   4 +-
 .../binary/ClientBinaryTypeGetRequest.java      |   6 +-
 .../binary/ClientBinaryTypeGetResponse.java     |   4 +-
 .../binary/ClientBinaryTypeNameGetRequest.java  |   6 +-
 .../binary/ClientBinaryTypeNamePutRequest.java  |   6 +-
 .../binary/ClientBinaryTypePutRequest.java      |   6 +-
 .../client/cache/ClientCacheGetRequest.java     |   4 +-
 .../client/cache/ClientCachePutRequest.java     |   4 +-
 .../client/cache/ClientCacheRequest.java        |  37 ++-
 .../cache/ClientCacheScanQueryCursor.java       | 135 +++++++++++
 .../ClientCacheScanQueryNextPageRequest.java    |  49 ++++
 .../ClientCacheScanQueryNextPageResponse.java   |  50 ++++
 .../cache/ClientCacheScanQueryRequest.java      | 143 +++++++++++
 .../cache/ClientCacheScanQueryResponse.java     |  52 ++++
 .../Apache.Ignite.Core.Tests.csproj             |   8 +-
 .../Client/Cache/CacheTest.cs                   | 154 ++++++++++++
 .../Client/Cache/CacheTestNoMeta.cs             | 138 +++++++++++
 .../Client/Cache/Person.cs                      |  48 ++++
 .../Client/Cache/ScanQueryTest.cs               | 241 +++++++++++++++++++
 .../Client/CacheTest.cs                         | 200 ---------------
 .../Client/CacheTestNoMeta.cs                   | 159 ------------
 .../Client/ClientTestBase.cs                    | 113 +++++++++
 .../Apache.Ignite.Core.Tests/Client/Person.cs   |  48 ----
 .../Client/RawSocketTest.cs                     |   3 +
 .../Apache.Ignite.Core.csproj                   |   2 +
 .../Apache.Ignite.Core/Cache/Query/ScanQuery.cs |   2 +-
 .../Client/Cache/ICacheClient.cs                |   9 +
 .../Impl/Cache/Query/QueryCursorBase.cs         |  52 ++--
 .../Impl/Client/Cache/CacheClient.cs            |  49 ++++
 .../Impl/Client/Cache/CacheFlags.cs             |  38 +++
 .../Client/Cache/Query/ClientQueryCursor.cs     |  94 ++++++++
 .../Apache.Ignite.Core/Impl/Client/ClientOp.cs  |   5 +-
 .../Impl/Client/ClientSocket.cs                 |  12 +-
 .../Impl/Client/IgniteClient.cs                 |  11 +-
 48 files changed, 1743 insertions(+), 484 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
index e8ba18d..f0f4903 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
@@ -236,7 +236,7 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<byte
                 return new JdbcConnectionContext(ctx, busyLock, maxCursors);
 
             case THIN_CLIENT:
-                return new ClientConnectionContext(ctx);
+                return new ClientConnectionContext(ctx, maxCursors);
 
             default:
                 throw new IgniteException("Unknown client type: " + 
clientType);

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
index fe50bb5..2143fb6f 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
@@ -235,9 +235,10 @@ public class JdbcRequestHandler implements 
ClientListenerRequestHandler {
         int cursorCnt = qryCursors.size();
 
         if (maxCursors > 0 && cursorCnt >= maxCursors)
-            return new JdbcResponse(IgniteQueryErrorCode.UNKNOWN, "Too many 
opened cursors (either close other " +
-                "opened cursors or increase the limit through 
OdbcConfiguration.setMaxOpenCursors()) " +
-                "[maximum=" + maxCursors + ", current=" + cursorCnt + ']');
+            return new JdbcResponse(IgniteQueryErrorCode.UNKNOWN, "Too many 
open cursors (either close other " +
+                "open cursors or increase the limit through " +
+                "ClientConnectorConfiguration.maxOpenCursorsPerConnection) 
[maximum=" + maxCursors +
+                ", current=" + cursorCnt + ']');
 
         long qryId = QRY_ID_GEN.getAndIncrement();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
index b66b1af..cca991d 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
@@ -219,9 +219,10 @@ public class OdbcRequestHandler implements 
ClientListenerRequestHandler {
         int cursorCnt = qryCursors.size();
 
         if (maxCursors > 0 && cursorCnt >= maxCursors)
-            return new OdbcResponse(ClientListenerResponse.STATUS_FAILED, "Too 
many opened cursors (either close other " +
-                "opened cursors or increase the limit through 
OdbcConfiguration.setMaxOpenCursors()) " +
-                "[maximum=" + maxCursors + ", current=" + cursorCnt + ']');
+            return new OdbcResponse(ClientListenerResponse.STATUS_FAILED, "Too 
many open cursors (either close " +
+                "other open cursors or increase the limit through " +
+                "ClientConnectorConfiguration.maxOpenCursorsPerConnection) 
[maximum=" + maxCursors +
+                ", current=" + cursorCnt + ']');
 
         long qryId = QRY_ID_GEN.getAndIncrement();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/query/PlatformAbstractQueryCursor.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/query/PlatformAbstractQueryCursor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/query/PlatformAbstractQueryCursor.java
index f201425..20e98fc 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/query/PlatformAbstractQueryCursor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cache/query/PlatformAbstractQueryCursor.java
@@ -63,7 +63,7 @@ public abstract class PlatformAbstractQueryCursor<T> extends 
PlatformAbstractTar
      * @param cursor Underlying cursor.
      * @param batchSize Batch size.
      */
-    public PlatformAbstractQueryCursor(PlatformContext platformCtx, 
QueryCursorEx<T> cursor, int batchSize) {
+    PlatformAbstractQueryCursor(PlatformContext platformCtx, QueryCursorEx<T> 
cursor, int batchSize) {
         super(platformCtx);
 
         this.cursor = cursor;
@@ -88,6 +88,11 @@ public abstract class PlatformAbstractQueryCursor<T> extends 
PlatformAbstractTar
                     }
 
                     writer.writeInt(cntPos, cnt);
+
+                    writer.writeBoolean(iter.hasNext());
+
+                    if (!iter.hasNext())
+                        cursor.close();
                 }
                 catch (Exception err) {
                     throw PlatformUtils.unwrapQueryException(err);
@@ -188,7 +193,7 @@ public abstract class PlatformAbstractQueryCursor<T> 
extends PlatformAbstractTar
          *
          * @param writer Writer.
          */
-        public Consumer(PlatformAbstractQueryCursor<T> cursor, 
BinaryRawWriterEx writer) {
+        Consumer(PlatformAbstractQueryCursor<T> cursor, BinaryRawWriterEx 
writer) {
             this.cursor = cursor;
             this.writer = writer;
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBooleanResponse.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBooleanResponse.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBooleanResponse.java
index 7ab3dc7..311ee01 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBooleanResponse.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientBooleanResponse.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.processors.platform.client;
 
-import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
 
 /**
  * Boolean response.
@@ -38,7 +38,7 @@ public class ClientBooleanResponse extends ClientResponse {
     }
 
     /** {@inheritDoc} */
-    @Override public void encode(BinaryRawWriter writer) {
+    @Override public void encode(BinaryRawWriterEx writer) {
         super.encode(writer);
 
         writer.writeBoolean(val);

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientCloseableResource.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientCloseableResource.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientCloseableResource.java
new file mode 100644
index 0000000..7498d65
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientCloseableResource.java
@@ -0,0 +1,28 @@
+/*
+ * 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.platform.client;
+
+/**
+ * Client closeable resource.
+ */
+public interface ClientCloseableResource {
+    /**
+     * Closes the resource.
+     */
+    void close();
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
index 6a3e98e..7bab76f 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.platform.client;
 
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryReaderExImpl;
 import 
org.apache.ignite.internal.processors.odbc.ClientListenerConnectionContext;
@@ -24,6 +25,8 @@ import 
org.apache.ignite.internal.processors.odbc.ClientListenerMessageParser;
 import 
org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion;
 import org.apache.ignite.internal.processors.odbc.ClientListenerRequestHandler;
 
+import java.util.concurrent.atomic.AtomicLong;
+
 /**
  * Thin Client connection context.
  */
@@ -37,16 +40,50 @@ public class ClientConnectionContext implements 
ClientListenerConnectionContext
     /** Request handler. */
     private final ClientRequestHandler handler;
 
+    /** Handle registry. */
+    private final ClientResourceRegistry resReg = new ClientResourceRegistry();
+
+    /** Kernal context. */
+    private final GridKernalContext kernalCtx;
+
+    /** Max cursors. */
+    private final int maxCursors;
+
+    /** Cursor counter. */
+    private final AtomicLong curCnt = new AtomicLong();
+
     /**
      * Ctor.
      *
      * @param ctx Kernal context.
+     * @param maxCursors Max active cursors.
      */
-    public ClientConnectionContext(GridKernalContext ctx) {
+    public ClientConnectionContext(GridKernalContext ctx, int maxCursors) {
         assert ctx != null;
 
+        kernalCtx = ctx;
+
         parser = new ClientMessageParser(ctx);
-        handler = new ClientRequestHandler(ctx);
+        handler = new ClientRequestHandler(this);
+        this.maxCursors = maxCursors;
+    }
+
+    /**
+     * Gets the handle registry.
+     *
+     * @return Handle registry.
+     */
+    public ClientResourceRegistry resources() {
+        return resReg;
+    }
+
+    /**
+     * Gets the kernal context.
+     *
+     * @return Kernal context.
+     */
+    public GridKernalContext kernalContext() {
+        return kernalCtx;
     }
 
     /** {@inheritDoc} */
@@ -76,6 +113,28 @@ public class ClientConnectionContext implements 
ClientListenerConnectionContext
 
     /** {@inheritDoc} */
     @Override public void onDisconnected() {
-        // No-op.
+        resReg.clean();
+    }
+
+    /**
+     * Increments the cursor count.
+     */
+    public void incrementCursors() {
+        long curCnt0 = curCnt.get();
+
+        if (curCnt0 >= maxCursors) {
+            throw new IgniteException("Too many open cursors (either close 
other open cursors or increase the " +
+                "limit through 
ClientConnectorConfiguration.maxOpenCursorsPerConnection) [maximum=" + 
maxCursors +
+                ", current=" + curCnt0 + ']');
+        }
+
+        curCnt.incrementAndGet();
+    }
+
+    /**
+     * Increments the cursor count.
+     */
+    public void decrementCursors() {
+        curCnt.decrementAndGet();
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
index f252a38..219afdc 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientMessageParser.java
@@ -18,9 +18,9 @@
 package org.apache.ignite.internal.processors.platform.client;
 
 import org.apache.ignite.IgniteException;
-import org.apache.ignite.binary.BinaryRawWriter;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
 import org.apache.ignite.internal.binary.GridBinaryMarshaller;
 import org.apache.ignite.internal.binary.streams.BinaryHeapInputStream;
 import org.apache.ignite.internal.binary.streams.BinaryHeapOutputStream;
@@ -35,6 +35,8 @@ import 
org.apache.ignite.internal.processors.platform.client.binary.ClientBinary
 import 
org.apache.ignite.internal.processors.platform.client.binary.ClientBinaryTypeNamePutRequest;
 import 
org.apache.ignite.internal.processors.platform.client.cache.ClientCacheGetRequest;
 import 
org.apache.ignite.internal.processors.platform.client.cache.ClientCachePutRequest;
+import 
org.apache.ignite.internal.processors.platform.client.cache.ClientCacheScanQueryNextPageRequest;
+import 
org.apache.ignite.internal.processors.platform.client.cache.ClientCacheScanQueryRequest;
 
 /**
  * Thin client message parser.
@@ -58,6 +60,15 @@ public class ClientMessageParser implements 
ClientListenerMessageParser {
     /** */
     private static final short OP_PUT_BINARY_TYPE = 6;
 
+    /** */
+    private static final short OP_QUERY_SCAN = 7;
+
+    /** */
+    private static final short OP_QUERY_SCAN_CURSOR_GET_PAGE = 8;
+
+    /** */
+    private static final short OP_RESOURCE_CLOSE = 9;
+
     /** Marshaller. */
     private final GridBinaryMarshaller marsh;
 
@@ -100,6 +111,15 @@ public class ClientMessageParser implements 
ClientListenerMessageParser {
 
             case OP_PUT_BINARY_TYPE:
                 return new ClientBinaryTypePutRequest(reader);
+
+            case OP_QUERY_SCAN:
+                return new ClientCacheScanQueryRequest(reader);
+
+            case OP_QUERY_SCAN_CURSOR_GET_PAGE:
+                return new ClientCacheScanQueryNextPageRequest(reader);
+
+            case OP_RESOURCE_CLOSE:
+                return new ClientResourceCloseRequest(reader);
         }
 
         throw new IgniteException("Invalid operation: " + opCode);
@@ -109,10 +129,10 @@ public class ClientMessageParser implements 
ClientListenerMessageParser {
     @Override public byte[] encode(ClientListenerResponse resp) {
         BinaryHeapOutputStream outStream = new BinaryHeapOutputStream(32);
 
-        BinaryRawWriter writer = marsh.writer(outStream);
+        BinaryRawWriterEx writer = marsh.writer(outStream);
 
         ((ClientResponse)resp).encode(writer);
 
-        return outStream.array();
+        return outStream.arrayCopy();
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientObjectResponse.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientObjectResponse.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientObjectResponse.java
index fc851a3..14d1eef 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientObjectResponse.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientObjectResponse.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.processors.platform.client;
 
-import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
 
 /**
  * Single object response.
@@ -38,7 +38,7 @@ public class ClientObjectResponse extends ClientResponse {
     }
 
     /** {@inheritDoc} */
-    @Override public void encode(BinaryRawWriter writer) {
+    @Override public void encode(BinaryRawWriterEx writer) {
         super.encode(writer);
 
         writer.writeObject(val);

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequest.java
index 56d5b0f..788d2e0 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequest.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.processors.platform.client;
 
 import org.apache.ignite.binary.BinaryRawReader;
-import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.odbc.ClientListenerRequest;
 
 /**
@@ -47,7 +46,7 @@ public class ClientRequest implements ClientListenerRequest {
      *
      * @return Response.
      */
-    public ClientResponse process(GridKernalContext ctx) {
+    public ClientResponse process(ClientConnectionContext ctx) {
         return new ClientResponse(reqId);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
index 4dcda23..499fb05 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.processors.platform.client;
 
-import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryWriterExImpl;
 import org.apache.ignite.internal.processors.odbc.ClientListenerRequest;
 import org.apache.ignite.internal.processors.odbc.ClientListenerRequestHandler;
@@ -27,15 +26,15 @@ import 
org.apache.ignite.internal.processors.odbc.ClientListenerResponse;
  * Thin client request handler.
  */
 public class ClientRequestHandler implements ClientListenerRequestHandler {
-    /** Kernal context. */
-    private final GridKernalContext ctx;
+    /** Client context. */
+    private final ClientConnectionContext ctx;
 
     /**
      * Constructor.
      *
      * @param ctx Kernal context.
      */
-    ClientRequestHandler(GridKernalContext ctx) {
+    ClientRequestHandler(ClientConnectionContext ctx) {
         assert ctx != null;
 
         this.ctx = ctx;
@@ -43,7 +42,12 @@ public class ClientRequestHandler implements 
ClientListenerRequestHandler {
 
     /** {@inheritDoc} */
     @Override public ClientListenerResponse handle(ClientListenerRequest req) {
-        return ((ClientRequest)req).process(ctx);
+        try {
+            return ((ClientRequest)req).process(ctx);
+        }
+        catch (Throwable e) {
+            return new ClientResponse(req.requestId(), e.getMessage());
+        }
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceCloseRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceCloseRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceCloseRequest.java
new file mode 100644
index 0000000..1d1c5d8
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceCloseRequest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.platform.client;
+
+import org.apache.ignite.binary.BinaryRawReader;
+
+/**
+ * Resource close request.
+ */
+public class ClientResourceCloseRequest extends ClientRequest {
+    /** Resource id. */
+    private final long resId;
+
+    /**
+     * Constructor.
+     *
+     * @param reader Reader.
+     */
+    ClientResourceCloseRequest(BinaryRawReader reader) {
+        super(reader);
+
+        resId = reader.readLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        ctx.resources().release(resId);
+
+        return new ClientResponse(requestId());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceRegistry.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceRegistry.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceRegistry.java
new file mode 100644
index 0000000..6973122
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResourceRegistry.java
@@ -0,0 +1,98 @@
+/*
+ * 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.platform.client;
+
+import org.apache.ignite.IgniteException;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Per-connection resource registry.
+ */
+public class ClientResourceRegistry {
+    /** Handles. */
+    private final Map<Long, Object> res = new ConcurrentHashMap<>();
+
+    /** ID generator. */
+    private final AtomicLong idGen = new AtomicLong();
+
+    /**
+     * Allocates server handle for an object.
+     *
+     * @param obj Object.
+     * @return Handle.
+     */
+    public long put(Object obj) {
+        long id = idGen.incrementAndGet();
+
+        res.put(id, obj);
+
+        return id;
+    }
+
+    /**
+     * Gets the object by handle.
+     *
+     * @param hnd Handle.
+     * @param <T> Object type.
+     * @return Object.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T get(long hnd) {
+        Object obj = res.get(hnd);
+
+        if (obj == null)
+            throw new IgniteException("Failed to find resource with id: " + 
hnd);
+
+        return (T) obj;
+    }
+
+    /**
+     * Releases the handle.
+     *
+     * @param hnd Handle.
+     */
+    public void release(long hnd) {
+        Object obj = res.remove(hnd);
+
+        if (obj == null)
+            throw new IgniteException("Failed to find resource with id: " + 
hnd);
+
+        closeIfNeeded(obj);
+    }
+
+    /**
+     * Cleans all handles and closes all ClientCloseableResources.
+     */
+    public void clean() {
+        for (Map.Entry e : res.entrySet())
+            closeIfNeeded(e.getValue());
+    }
+
+    /**
+     * Close resource if needed.
+     *
+     * @param res Resource.
+     */
+    private static void closeIfNeeded(Object res) {
+        if (res instanceof ClientCloseableResource)
+            ((ClientCloseableResource)res).close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResponse.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResponse.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResponse.java
index 1089b48..a1bc4b6 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResponse.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientResponse.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.processors.platform.client;
 
-import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
 import org.apache.ignite.internal.processors.odbc.ClientListenerResponse;
 
 /**
@@ -39,9 +39,27 @@ public class ClientResponse extends ClientListenerResponse {
     }
 
     /**
+     * Constructor.
+     *
+     * @param reqId Request id.
+     */
+    public ClientResponse(long reqId, String err) {
+        super(STATUS_FAILED, err);
+
+        this.reqId = reqId;
+    }
+
+    /**
      * Encodes the response data.
      */
-    public void encode(BinaryRawWriter writer) {
+    public void encode(BinaryRawWriterEx writer) {
         writer.writeLong(reqId);
+
+        if (status() == STATUS_SUCCESS) {
+            writer.writeBoolean(true);
+        } else {
+            writer.writeBoolean(false);
+            writer.writeString(error());
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientStringResponse.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientStringResponse.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientStringResponse.java
index d9f955d..4ff476d 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientStringResponse.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientStringResponse.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.processors.platform.client;
 
-import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
 
 /**
  * Single string response.
@@ -39,7 +39,7 @@ public class ClientStringResponse extends ClientResponse {
     }
 
     /** {@inheritDoc} */
-    @Override public void encode(BinaryRawWriter writer) {
+    @Override public void encode(BinaryRawWriterEx writer) {
         super.encode(writer);
 
         writer.writeString(val);

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetRequest.java
index be64e91..72f9f58 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetRequest.java
@@ -18,8 +18,8 @@
 package org.apache.ignite.internal.processors.platform.client.binary;
 
 import org.apache.ignite.binary.BinaryRawReader;
-import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryTypeImpl;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientRequest;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 
@@ -42,8 +42,8 @@ public class ClientBinaryTypeGetRequest extends ClientRequest 
{
     }
 
     /** {@inheritDoc} */
-    @Override public ClientResponse process(GridKernalContext ctx) {
-        BinaryTypeImpl type = (BinaryTypeImpl) 
ctx.cacheObjects().binary().type(typeId);
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        BinaryTypeImpl type = 
(BinaryTypeImpl)ctx.kernalContext().cacheObjects().binary().type( typeId);
 
         return new ClientBinaryTypeGetResponse(requestId(), type.metadata());
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetResponse.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetResponse.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetResponse.java
index 0f44cbf..e888305 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetResponse.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeGetResponse.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.processors.platform.client.binary;
 
-import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
 import org.apache.ignite.internal.binary.BinaryMetadata;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
@@ -43,7 +43,7 @@ public class ClientBinaryTypeGetResponse extends 
ClientResponse {
     }
 
     /** {@inheritDoc} */
-    @Override public void encode(BinaryRawWriter writer) {
+    @Override public void encode(BinaryRawWriterEx writer) {
         super.encode(writer);
 
         PlatformUtils.writeBinaryMetadata(writer, meta, true);

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNameGetRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNameGetRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNameGetRequest.java
index 37f4d9e..b07cbad 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNameGetRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNameGetRequest.java
@@ -20,7 +20,7 @@ package 
org.apache.ignite.internal.processors.platform.client.binary;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.binary.BinaryRawReader;
-import org.apache.ignite.internal.GridKernalContext;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientRequest;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 import 
org.apache.ignite.internal.processors.platform.client.ClientStringResponse;
@@ -48,9 +48,9 @@ public class ClientBinaryTypeNameGetRequest extends 
ClientRequest {
     }
 
     /** {@inheritDoc} */
-    @Override public ClientResponse process(GridKernalContext ctx) {
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
         try {
-            String typeName = ctx.marshallerContext().getClassName(platformId, 
typeId);
+            String typeName = 
ctx.kernalContext().marshallerContext().getClassName(platformId, typeId);
 
             return new ClientStringResponse(requestId(), typeName);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNamePutRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNamePutRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNamePutRequest.java
index 268e13b..3119fbb 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNamePutRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypeNamePutRequest.java
@@ -20,8 +20,8 @@ package 
org.apache.ignite.internal.processors.platform.client.binary;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.binary.BinaryRawReader;
-import org.apache.ignite.internal.GridKernalContext;
 import 
org.apache.ignite.internal.processors.platform.client.ClientBooleanResponse;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientRequest;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 
@@ -52,9 +52,9 @@ public class ClientBinaryTypeNamePutRequest extends 
ClientRequest {
     }
 
     /** {@inheritDoc} */
-    @Override public ClientResponse process(GridKernalContext ctx) {
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
         try {
-            boolean res = 
ctx.marshallerContext().registerClassName(platformId, typeId, typeName);
+            boolean res = 
ctx.kernalContext().marshallerContext().registerClassName(platformId, typeId, 
typeName);
 
             return new ClientBooleanResponse(requestId(), res);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypePutRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypePutRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypePutRequest.java
index aeb6e02..7839d48 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypePutRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/binary/ClientBinaryTypePutRequest.java
@@ -17,11 +17,11 @@
 
 package org.apache.ignite.internal.processors.platform.client.binary;
 
-import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryContext;
 import org.apache.ignite.internal.binary.BinaryMetadata;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
 import 
org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientRequest;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
@@ -46,8 +46,8 @@ public class ClientBinaryTypePutRequest extends ClientRequest 
{
 
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
-    @Override public ClientResponse process(GridKernalContext ctx) {
-        BinaryContext binCtx = ((CacheObjectBinaryProcessorImpl) 
ctx.cacheObjects()).binaryContext();
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        BinaryContext binCtx = ((CacheObjectBinaryProcessorImpl) 
ctx.kernalContext().cacheObjects()).binaryContext();
 
         binCtx.updateMetadata(meta.typeId(), meta);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheGetRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheGetRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheGetRequest.java
index 8d8487f..e2d261a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheGetRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheGetRequest.java
@@ -17,8 +17,8 @@
 
 package org.apache.ignite.internal.processors.platform.client.cache;
 
-import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import 
org.apache.ignite.internal.processors.platform.client.ClientObjectResponse;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 
@@ -42,7 +42,7 @@ public class ClientCacheGetRequest extends ClientCacheRequest 
{
 
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
-    @Override public ClientResponse process(GridKernalContext ctx) {
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
         Object val = cache(ctx).get(key);
 
         return new ClientObjectResponse(requestId(), val);

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCachePutRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCachePutRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCachePutRequest.java
index 5597a07..04e3961 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCachePutRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCachePutRequest.java
@@ -17,8 +17,8 @@
 
 package org.apache.ignite.internal.processors.platform.client.cache;
 
-import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientResponse;
 
 /**
@@ -45,7 +45,7 @@ public class ClientCachePutRequest extends ClientCacheRequest 
{
 
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
-    @Override public ClientResponse process(GridKernalContext ctx) {
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
         cache(ctx).put(key, val);
 
         return super.process(ctx);

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
index 5232735..fd7cc6c 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheRequest.java
@@ -19,16 +19,22 @@ package 
org.apache.ignite.internal.processors.platform.client.cache;
 
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.binary.BinaryRawReader;
-import org.apache.ignite.internal.GridKernalContext;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
 import org.apache.ignite.internal.processors.platform.client.ClientRequest;
 
 /**
  * Cache get request.
  */
 class ClientCacheRequest extends ClientRequest {
+    /** Flag: keep binary. */
+    private static final byte FLAG_KEEP_BINARY = 1;
+
     /** Cache ID. */
     private final int cacheId;
 
+    /** Flags. */
+    private final byte flags;
+
     /**
      * Constructor.
      *
@@ -39,18 +45,37 @@ class ClientCacheRequest extends ClientRequest {
 
         cacheId = reader.readInt();
 
-        reader.readByte();  // Flags (skipStore, etc);
+        flags = reader.readByte();
+    }
+
+    /**
+     * Gets the cache for current cache id, with binary mode enabled.
+     *
+     * @param ctx Kernal context.
+     * @return Cache.
+     */
+    protected IgniteCache cache(ClientConnectionContext ctx) {
+        return rawCache(ctx).withKeepBinary();
+    }
+
+    /**
+     *  Gets a value indicating whether keepBinary flag is set in this request.
+     *
+     * @return keepBinary flag value.
+     */
+    protected boolean isKeepBinary() {
+        return (flags & FLAG_KEEP_BINARY) == FLAG_KEEP_BINARY;
     }
 
     /**
-     * Gets the cache for current cache id.
+     * Gets the cache for current cache id, ignoring any flags.
      *
      * @param ctx Kernal context.
      * @return Cache.
      */
-    protected IgniteCache cache(GridKernalContext ctx) {
-        String cacheName = 
ctx.cache().context().cacheContext(cacheId).cache().name();
+    protected IgniteCache rawCache(ClientConnectionContext ctx) {
+        String cacheName = 
ctx.kernalContext().cache().context().cacheContext(cacheId).cache().name();
 
-        return ctx.grid().cache(cacheName).withKeepBinary();
+        return ctx.kernalContext().grid().cache(cacheName);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java
new file mode 100644
index 0000000..9d3d158
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryCursor.java
@@ -0,0 +1,135 @@
+/*
+ * 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.platform.client.cache;
+
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
+import 
org.apache.ignite.internal.processors.platform.client.ClientCloseableResource;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+
+import javax.cache.Cache;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Query cursor holder.
+  */
+class ClientCacheScanQueryCursor implements ClientCloseableResource {
+    /** Cursor. */
+    private final QueryCursorEx<Cache.Entry> cursor;
+
+    /** Page size. */
+    private final int pageSize;
+
+    /** Context. */
+    private final ClientConnectionContext ctx;
+
+    /** Id. */
+    private long id;
+
+    /** Iterator. */
+    private Iterator<Cache.Entry> iterator;
+
+    /** Close guard. */
+    private final AtomicBoolean closeGuard = new AtomicBoolean();
+
+    /**
+     * Ctor.
+     *  @param cursor Cursor.
+     * @param pageSize Page size.
+     * @param ctx Context.
+     */
+    ClientCacheScanQueryCursor(QueryCursorEx<Cache.Entry> cursor, int 
pageSize, ClientConnectionContext ctx) {
+        assert cursor != null;
+        assert pageSize > 0;
+        assert ctx != null;
+
+        this.cursor = cursor;
+        this.pageSize = pageSize;
+        this.ctx = ctx;
+    }
+
+    /**
+     * Writes next page to the writer.
+     *
+     * @param writer Writer.
+     */
+    void writePage(BinaryRawWriterEx writer) {
+        Iterator<Cache.Entry> iter = iterator();
+
+        int cntPos = writer.reserveInt();
+        int cnt = 0;
+
+        while (cnt < pageSize && iter.hasNext()) {
+            Cache.Entry e = iter.next();
+
+            writer.writeObjectDetached(e.getKey());
+            writer.writeObjectDetached(e.getValue());
+
+            cnt++;
+        }
+
+        writer.writeInt(cntPos, cnt);
+
+        writer.writeBoolean(iter.hasNext());
+
+        if (!iter.hasNext())
+            ctx.resources().release(id);
+    }
+
+    /**
+     * Closes the cursor.
+     */
+    @Override public void close() {
+        if (closeGuard.compareAndSet(false, true)) {
+            cursor.close();
+
+            ctx.decrementCursors();
+        }
+    }
+
+    /**
+     * Sets the cursor id.
+     *
+     * @param id Id.
+     */
+    public void id(long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets the cursor id.
+     *
+     * @return Id.
+     */
+    public long id() {
+        return id;
+    }
+
+    /**
+     * Gets the iterator.
+     *
+     * @return Iterator.
+     */
+    private Iterator<Cache.Entry> iterator() {
+        if (iterator == null)
+            iterator = cursor.iterator();
+
+        return iterator;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java
new file mode 100644
index 0000000..a9620d2
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageRequest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.platform.client.cache;
+
+import org.apache.ignite.binary.BinaryRawReader;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+import org.apache.ignite.internal.processors.platform.client.ClientRequest;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+/**
+ * Query cursor next page request.
+ */
+public class ClientCacheScanQueryNextPageRequest extends ClientRequest {
+    /** Cursor id. */
+    private final long cursorId;
+
+    /**
+     * Ctor.
+     *
+     * @param reader Reader.
+     */
+    public ClientCacheScanQueryNextPageRequest(BinaryRawReader reader) {
+        super(reader);
+
+        cursorId = reader.readLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        ClientCacheScanQueryCursor cur = ctx.resources().get(cursorId);
+
+        return new ClientCacheScanQueryNextPageResponse(requestId(), cur);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java
new file mode 100644
index 0000000..e4ffe6d
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryNextPageResponse.java
@@ -0,0 +1,50 @@
+/*
+ * 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.platform.client.cache;
+
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+/**
+ * Query cursor next page response.
+ */
+class ClientCacheScanQueryNextPageResponse extends ClientResponse {
+    /** Cursor. */
+    private final ClientCacheScanQueryCursor cursor;
+
+    /**
+     * Ctor.
+     *
+     * @param requestId Request id.
+     * @param cursor Cursor.
+     */
+    ClientCacheScanQueryNextPageResponse(long requestId, 
ClientCacheScanQueryCursor cursor) {
+        super(requestId);
+
+        assert cursor != null;
+
+        this.cursor = cursor;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void encode(BinaryRawWriterEx writer) {
+        super.encode(writer);
+
+        cursor.writePage(writer);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
new file mode 100644
index 0000000..7c163e3
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryRequest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.platform.client.cache;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.internal.binary.BinaryRawReaderEx;
+import org.apache.ignite.internal.processors.cache.query.QueryCursorEx;
+import org.apache.ignite.internal.processors.platform.PlatformContext;
+import 
org.apache.ignite.internal.processors.platform.client.ClientConnectionContext;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
+import org.apache.ignite.lang.IgniteBiPredicate;
+
+/**
+ * Scan query request.
+ */
+@SuppressWarnings("unchecked")
+public class ClientCacheScanQueryRequest extends ClientCacheRequest {
+    /** Java filter. */
+    private static final byte FILTER_PLATFORM_JAVA = 1;
+
+    /** .NET filter. */
+    private static final byte FILTER_PLATFORM_DOTNET = 2;
+
+    /** C++ filter. */
+    private static final byte FILTER_PLATFORM_CPP = 3;
+
+    /** Local flag. */
+    private final boolean loc;
+
+    /** Page size. */
+    private final int pageSize;
+
+    /** Partition. */
+    private final Integer part;
+
+    /** Filter platform. */
+    private final byte filterPlatform;
+
+    /** Filter object. */
+    private final Object filterObj;
+
+    /**
+     * Ctor.
+     *
+     * @param reader Reader.
+     */
+    public ClientCacheScanQueryRequest(BinaryRawReaderEx reader) {
+        super(reader);
+
+        filterObj = reader.readObjectDetached();
+
+        filterPlatform = filterObj == null ? 0 : reader.readByte();
+
+        pageSize = reader.readInt();
+
+        int part0 = reader.readInt();
+        part = part0 < 0 ? null : part0;
+
+        loc = reader.readBoolean();
+    }
+
+    /** {@inheritDoc} */
+    @Override public ClientResponse process(ClientConnectionContext ctx) {
+        IgniteCache cache = filterPlatform == FILTER_PLATFORM_JAVA && 
!isKeepBinary() ? rawCache(ctx) : cache(ctx);
+
+        ScanQuery qry = new ScanQuery()
+            .setLocal(loc)
+            .setPageSize(pageSize)
+            .setPartition(part)
+            .setFilter(createFilter(ctx));
+
+        ctx.incrementCursors();
+
+        try {
+            QueryCursor cur = cache.query(qry);
+
+            ClientCacheScanQueryCursor cliCur = new 
ClientCacheScanQueryCursor((QueryCursorEx)cur, pageSize, ctx);
+
+            long cursorId = ctx.resources().put(cliCur);
+
+            cliCur.id(cursorId);
+
+            return new ClientCacheScanQueryResponse(requestId(), cliCur);
+        }
+        catch (Exception e) {
+            ctx.decrementCursors();
+
+            throw e;
+        }
+    }
+
+    /**
+     * Creates the filter.
+     *
+     * @return Filter.
+     * @param ctx Context.
+     */
+    private IgniteBiPredicate createFilter(ClientConnectionContext ctx) {
+        if (filterObj == null)
+            return null;
+
+        switch (filterPlatform) {
+            case FILTER_PLATFORM_JAVA:
+                return ((BinaryObject)filterObj).deserialize();
+
+            case FILTER_PLATFORM_DOTNET:
+                PlatformContext platformCtx = 
ctx.kernalContext().platform().context();
+
+                String curPlatform = platformCtx.platform();
+
+                if (!PlatformUtils.PLATFORM_DOTNET.equals(curPlatform)) {
+                    throw new IgniteException("ScanQuery filter platform is " 
+ PlatformUtils.PLATFORM_DOTNET +
+                        ", current platform is " + curPlatform);
+                }
+
+                return platformCtx.createCacheEntryFilter(filterObj, 0);
+
+            case FILTER_PLATFORM_CPP:
+            default:
+                throw new UnsupportedOperationException("Invalid client 
ScanQuery filter code: " + filterPlatform);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java
new file mode 100644
index 0000000..0623804
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/cache/ClientCacheScanQueryResponse.java
@@ -0,0 +1,52 @@
+/*
+ * 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.platform.client.cache;
+
+import org.apache.ignite.internal.binary.BinaryRawWriterEx;
+import org.apache.ignite.internal.processors.platform.client.ClientResponse;
+
+/**
+ * Scan query response.
+ */
+class ClientCacheScanQueryResponse extends ClientResponse {
+    /** Cursor. */
+    private final ClientCacheScanQueryCursor cursor;
+
+    /**
+     * Ctor.
+     *
+     * @param requestId Request id.
+     * @param cursor Cursor.
+     */
+    ClientCacheScanQueryResponse(long requestId, ClientCacheScanQueryCursor 
cursor) {
+        super(requestId);
+
+        assert cursor != null;
+
+        this.cursor = cursor;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void encode(BinaryRawWriterEx writer) {
+        super.encode(writer);
+
+        writer.writeLong(cursor.id());
+
+        cursor.writePage(writer);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 31e95fb..9e6cfb2 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -92,10 +92,12 @@
     <Compile Include="Cache\Query\Linq\CacheLinqTest.Contains.cs" />
     <Compile Include="Cache\Store\CacheStoreSessionTestCodeConfig.cs" />
     <Compile Include="Cache\Store\CacheStoreSessionTestSharedFactory.cs" />
-    <Compile Include="Client\CacheTestNoMeta.cs" />
-    <Compile Include="Client\Person.cs" />
+    <Compile Include="Client\Cache\CacheTest.cs" />
+    <Compile Include="Client\Cache\CacheTestNoMeta.cs" />
+    <Compile Include="Client\Cache\ScanQueryTest.cs" />
+    <Compile Include="Client\Cache\Person.cs" />
+    <Compile Include="Client\ClientTestBase.cs" />
     <Compile Include="Client\RawSocketTest.cs" />
-    <Compile Include="Client\CacheTest.cs" />
     <Compile Include="Client\ClientConnectionTest.cs" />
     <Compile Include="Client\IgniteClientConfigurationTest.cs" />
     <Compile Include="Deployment\CacheGetFunc.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTest.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTest.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTest.cs
new file mode 100644
index 0000000..dc7bd7a
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTest.cs
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Client.Cache
+{
+    using System;
+    using System.Collections.Concurrent;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Threading;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Client;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Thin client cache test.
+    /// </summary>
+    public sealed class CacheTest : ClientTestBase
+    {
+        /// <summary>
+        /// Tests the cache put / get with primitive data types.
+        /// </summary>
+        [Test]
+        public void TestPutGetPrimitives()
+        {
+            using (var client = GetClient())
+            {
+                GetCache<string>().Put(1, "foo");
+
+                var clientCache = client.GetCache<int?, string>(CacheName);
+
+                clientCache.Put(2, "bar");
+                clientCache[3] = "baz";
+
+                // Existing key.
+                Assert.AreEqual("foo", clientCache.Get(1));
+                Assert.AreEqual("foo", clientCache[1]);
+                Assert.AreEqual("bar", clientCache[2]);
+                Assert.AreEqual("baz", clientCache[3]);
+
+                // Missing key.
+                Assert.Throws<KeyNotFoundException>(() => clientCache.Get(-1));
+
+                // Null key.
+                Assert.Throws<ArgumentNullException>(() => 
clientCache.Get(null));
+            }
+        }
+
+        /// <summary>
+        /// Tests the cache put / get with user data types.
+        /// </summary>
+        [Test]
+        public void TestPutGetUserObjects([Values(true, false)] bool 
compactFooter)
+        {
+            var cfg = GetClientConfiguration();
+
+            cfg.BinaryConfiguration = new BinaryConfiguration
+            {
+                CompactFooter = compactFooter
+            };
+
+            using (var client = Ignition.StartClient(cfg))
+            {
+                var person = new Person {Id = 100, Name = "foo"};
+                var person2 = new Person2 {Id = 200, Name = "bar"};
+                
+                var serverCache = GetCache<Person>();
+                var clientCache = client.GetCache<int?, Person>(CacheName);
+
+                Assert.AreEqual(CacheName, clientCache.Name);
+
+                // Put through server cache.
+                serverCache.Put(1, person);
+
+                // Put through client cache.
+                clientCache.Put(2, person2);
+                clientCache[3] = person2;
+
+                // Read from client cache.
+                Assert.AreEqual("foo", clientCache.Get(1).Name);
+                Assert.AreEqual(100, clientCache[1].Id);
+                Assert.AreEqual(200, clientCache[2].Id);
+                Assert.AreEqual(200, clientCache[3].Id);
+
+                // Read from server cache.
+                Assert.AreEqual("foo", serverCache.Get(1).Name);
+                Assert.AreEqual(100, serverCache[1].Id);
+                Assert.AreEqual(200, serverCache[2].Id);
+                Assert.AreEqual(200, serverCache[3].Id);
+
+                // Null key or value.
+                Assert.Throws<ArgumentNullException>(() => clientCache.Put(10, 
null));
+                Assert.Throws<ArgumentNullException>(() => 
clientCache.Put(null, person));
+            }
+        }
+
+        /// <summary>
+        /// Tests client get in multiple threads with a single client.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestGetMultithreadedSingleClient()
+        {
+            GetCache<string>().Put(1, "foo");
+
+            using (var client = GetClient())
+            {
+                var clientCache = client.GetCache<int, string>(CacheName);
+
+                TestUtils.RunMultiThreaded(() => Assert.AreEqual("foo", 
clientCache.Get(1)),
+                    Environment.ProcessorCount, 5);
+            }
+        }
+
+        /// <summary>
+        /// Tests client get in multiple threads with multiple clients.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestGetMultithreadedMultiClient()
+        {
+            GetCache<string>().Put(1, "foo");
+
+            // One client per thread.
+            var clients = new ConcurrentDictionary<int, IIgniteClient>();
+
+            TestUtils.RunMultiThreaded(() =>
+                {
+                    var client = 
clients.GetOrAdd(Thread.CurrentThread.ManagedThreadId, _ => GetClient());
+
+                    var clientCache = client.GetCache<int, string>(CacheName);
+
+                    Assert.AreEqual("foo", clientCache.Get(1));
+                },
+                Environment.ProcessorCount, 5);
+
+            clients.ToList().ForEach(x => x.Value.Dispose());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
new file mode 100644
index 0000000..6695835
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/CacheTestNoMeta.cs
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Client.Cache
+{
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Net;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache.Configuration;
+    using Apache.Ignite.Core.Cache.Query;
+    using Apache.Ignite.Core.Client;
+    using Apache.Ignite.Core.Impl.Binary;
+    using Apache.Ignite.Core.Impl.Binary.Metadata;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Client cache test without metadata (no-op binary processor).
+    /// </summary>
+    public class CacheTestNoMeta : ClientTestBase
+    {
+        /// <summary>
+        /// Tests the cache put / get with user data types.
+        /// </summary>
+        [Test]
+        public void TestPutGetUserObjects()
+        {
+            var cfg = new IgniteClientConfiguration
+            {
+                BinaryProcessor = new NoopBinaryProcessor(),
+                BinaryConfiguration = new BinaryConfiguration
+                {
+                    CompactFooter = false
+                },
+                Host = IPAddress.Loopback.ToString()
+            };
+
+            using (var client = Ignition.StartClient(cfg))
+            {
+                var serverCache = Ignition.GetIgnite().GetOrCreateCache<int?, 
Person>(
+                    new CacheConfiguration(CacheName, new QueryEntity
+                    {
+                        KeyType = typeof(int),
+                        ValueType = typeof(Person),
+                        Fields = new[]
+                        {
+                            new QueryField("id", typeof(int)),
+                            new QueryField("name", typeof(string))
+                        }
+                    }));
+
+                var clientCache = client.GetCache<int?, Person>(CacheName);
+
+                // Put through client cache.
+                clientCache.Put(1, new Person { Id = 100, Name = "foo" });
+                clientCache[2] = new Person { Id = 200, Name = "bar" };
+
+                // Read from client cache.
+                Assert.AreEqual("foo", clientCache.Get(1).Name);
+                Assert.AreEqual(100, clientCache[1].Id);
+                Assert.AreEqual(200, clientCache[2].Id);
+
+                // Read from server cache.
+                Assert.AreEqual("foo", serverCache.Get(1).Name);
+                Assert.AreEqual(100, serverCache[1].Id);
+                Assert.AreEqual(200, serverCache[2].Id);
+
+                // SQL from server cache.
+                var sqlRes = serverCache.Query(new SqlQuery(typeof(Person), 
"where id = 100")).GetAll().Single();
+                Assert.AreEqual(1, sqlRes.Key);
+                Assert.AreEqual(100, sqlRes.Value.Id);
+                Assert.AreEqual("foo", sqlRes.Value.Name);
+            }
+        }
+
+        /// <summary>
+        /// No-op binary processor (does not send meta to cluster).
+        /// </summary>
+        private class NoopBinaryProcessor : IBinaryProcessor
+        {
+            /** <inheritdoc /> */
+            public BinaryType GetBinaryType(int typeId)
+            {
+                return null;
+            }
+
+            /** <inheritdoc /> */
+            public List<IBinaryType> GetBinaryTypes()
+            {
+                return null;
+            }
+
+            /** <inheritdoc /> */
+            public int[] GetSchema(int typeId, int schemaId)
+            {
+                return null;
+            }
+
+            /** <inheritdoc /> */
+            public void PutBinaryTypes(ICollection<BinaryType> types)
+            {
+                // No-op.
+            }
+
+            /** <inheritdoc /> */
+            public bool RegisterType(int id, string typeName)
+            {
+                return false;
+            }
+
+            /** <inheritdoc /> */
+            public BinaryType RegisterEnum(string typeName, 
IEnumerable<KeyValuePair<string, int>> values)
+            {
+                return null;
+            }
+
+            /** <inheritdoc /> */
+            public string GetTypeName(int id)
+            {
+                return null;
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/86372dee/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs
new file mode 100644
index 0000000..a6bc9d7
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Cache/Person.cs
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Client.Cache
+{
+    /// <summary>
+    /// Test person.
+    /// </summary>
+    public class Person
+    {
+        /// <summary>
+        /// Gets or sets the identifier.
+        /// </summary>
+        public int Id { get; set; }
+
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets or sets the parent.
+        /// </summary>
+        public Person Parent { get;set; }
+    }
+
+    /// <summary>
+    /// Test person 2.
+    /// </summary>
+    public class Person2 : Person
+    {
+        // No-op.
+    }
+}

Reply via email to