ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From anovi...@apache.org
Subject [1/3] ignite git commit: IGNITE-3066 Limited Redis protocol implementation. - Fixes #1212.
Date Wed, 16 Nov 2016 10:58:15 GMT
Repository: ignite
Updated Branches:
  refs/heads/master 2c64dd062 -> f1e6257c5


http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetCommandHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetCommandHandler.java
new file mode 100644
index 0000000..8eb0b3f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetCommandHandler.java
@@ -0,0 +1,130 @@
+/*
+ * 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.handlers.redis.string;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.processors.rest.GridRestProtocolHandler;
+import org.apache.ignite.internal.processors.rest.GridRestResponse;
+import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisRestCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.exception.GridRedisGenericException;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisProtocolParser;
+import org.apache.ignite.internal.processors.rest.request.GridRestCacheRequest;
+import org.apache.ignite.internal.processors.rest.request.GridRestRequest;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_PUT;
+import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_PUT_IF_ABSENT;
+import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_REPLACE;
+import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand.SET;
+
+/**
+ * Redis SET command handler.
+ * <p>
+ * No key expiration is currently supported.
+ */
+public class GridRedisSetCommandHandler extends GridRedisRestCommandHandler {
+    /** Supported commands. */
+    private static final Collection<GridRedisCommand> SUPPORTED_COMMANDS = U.sealList(
+        SET
+    );
+
+    /** Value position in Redis message. */
+    private static final int VAL_POS = 2;
+
+    /** {@inheritDoc} */
+    public GridRedisSetCommandHandler(final IgniteLogger log, final GridRestProtocolHandler hnd) {
+        super(log, hnd);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<GridRedisCommand> supportedCommands() {
+        return SUPPORTED_COMMANDS;
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridRestRequest asRestRequest(GridRedisMessage msg) throws IgniteCheckedException {
+        assert msg != null;
+
+        GridRestCacheRequest restReq = new GridRestCacheRequest();
+
+        restReq.clientId(msg.clientId());
+        restReq.key(msg.key());
+
+        restReq.command(CACHE_PUT);
+
+        if (msg.messageSize() < 3)
+            throw new GridRedisGenericException("Wrong number of arguments");
+
+        restReq.value(msg.aux(VAL_POS));
+
+        if (msg.messageSize() >= 4) {
+
+            List<String> params = msg.aux();
+
+            // get rid of SET value.
+            params.remove(0);
+
+            if (isNx(params))
+                restReq.command(CACHE_PUT_IF_ABSENT);
+            else if (isXx(params))
+                restReq.command(CACHE_REPLACE);
+
+            // TODO: IGNITE-4226, handle expiration options.
+        }
+
+        return restReq;
+    }
+
+    /**
+     * @param params Command parameters.
+     * @return True if NX option is available, otherwise false.
+     */
+    private boolean isNx(List<String> params) {
+        if (params.size() >= 3)
+            return "nx".equalsIgnoreCase(params.get(0)) || "nx".equalsIgnoreCase(params.get(2));
+
+        return "nx".equalsIgnoreCase(params.get(0));
+    }
+
+    /**
+     * @param params Command parameters.
+     * @return True if XX option is available, otherwise false.
+     */
+    private boolean isXx(List<String> params) {
+        if (params.size() >= 3)
+            return "xx".equalsIgnoreCase(params.get(0)) || "xx".equalsIgnoreCase(params.get(2));
+
+        return "xx".equalsIgnoreCase(params.get(0));
+    }
+
+    /** {@inheritDoc} */
+    @Override public ByteBuffer makeResponse(final GridRestResponse restRes, List<String> params) {
+        Object resp = restRes.getResponse();
+
+        if (resp == null)
+            return GridRedisProtocolParser.nil();
+
+        return (!(boolean)resp ? GridRedisProtocolParser.nil() : GridRedisProtocolParser.oKString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetRangeCommandHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetRangeCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetRangeCommandHandler.java
new file mode 100644
index 0000000..dfd24a5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisSetRangeCommandHandler.java
@@ -0,0 +1,141 @@
+/*
+ * 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.handlers.redis.string;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.processors.rest.GridRestProtocolHandler;
+import org.apache.ignite.internal.processors.rest.GridRestResponse;
+import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisRestCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.exception.GridRedisGenericException;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisProtocolParser;
+import org.apache.ignite.internal.processors.rest.request.GridRestCacheRequest;
+import org.apache.ignite.internal.processors.rest.request.GridRestRequest;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_GET;
+import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_PUT;
+import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand.SETRANGE;
+
+/**
+ * Redis SETRANGE command handler.
+ */
+public class GridRedisSetRangeCommandHandler extends GridRedisRestCommandHandler {
+    /** Supported commands. */
+    private static final Collection<GridRedisCommand> SUPPORTED_COMMANDS = U.sealList(
+        SETRANGE
+    );
+
+    /** Offset position in Redis message among parameters. */
+    private static final int OFFSET_POS = 2;
+
+    /** Value position in Redis message. */
+    private static final int VAL_POS = 3;
+
+    /** Maximum offset. */
+    private static final int MAX_OFFSET = 536870911;
+
+    /** {@inheritDoc} */
+    public GridRedisSetRangeCommandHandler(final IgniteLogger log, final GridRestProtocolHandler hnd) {
+        super(log, hnd);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<GridRedisCommand> supportedCommands() {
+        return SUPPORTED_COMMANDS;
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridRestRequest asRestRequest(GridRedisMessage msg) throws IgniteCheckedException {
+        assert msg != null;
+
+        if (msg.messageSize() < 4)
+            throw new GridRedisGenericException("Wrong number of arguments");
+
+        int off;
+        try {
+            off = Integer.parseInt(msg.aux(OFFSET_POS));
+        }
+        catch (NumberFormatException e) {
+            U.error(log, "Erroneous offset", e);
+            throw new GridRedisGenericException("Offset is not an integer");
+        }
+
+        String val = String.valueOf(msg.aux(VAL_POS));
+
+        GridRestCacheRequest getReq = new GridRestCacheRequest();
+
+        getReq.clientId(msg.clientId());
+        getReq.key(msg.key());
+        getReq.command(CACHE_GET);
+
+        if (val.isEmpty())
+            return getReq;
+
+        Object resp = hnd.handle(getReq).getResponse();
+
+        int totalLen = off + val.length();
+        if (off < 0 || totalLen > MAX_OFFSET)
+            throw new GridRedisGenericException("Offset is out of range");
+
+        GridRestCacheRequest putReq = new GridRestCacheRequest();
+
+        putReq.clientId(msg.clientId());
+        putReq.key(msg.key());
+        putReq.command(CACHE_PUT);
+
+        if (resp == null) {
+            byte[] dst = new byte[totalLen];
+            System.arraycopy(val.getBytes(), 0, dst, off, val.length());
+
+            putReq.value(new String(dst));
+        }
+        else {
+            if (!(resp instanceof String))
+                return getReq;
+
+            String cacheVal = String.valueOf(resp);
+
+            cacheVal = cacheVal.substring(0, off) + val;
+
+            putReq.value(cacheVal);
+        }
+
+        hnd.handle(putReq);
+
+        return getReq;
+    }
+
+    /** {@inheritDoc} */
+    @Override public ByteBuffer makeResponse(final GridRestResponse restRes, List<String> params) {
+        if (restRes.getResponse() == null)
+            return GridRedisProtocolParser.toInteger("0");
+
+        if (restRes.getResponse() instanceof String) {
+            int resLen = ((String)restRes.getResponse()).length();
+            return GridRedisProtocolParser.toInteger(String.valueOf(resLen));
+        }
+        else
+            return GridRedisProtocolParser.toTypeError("Operation against a key holding the wrong kind of value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisStrlenCommandHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisStrlenCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisStrlenCommandHandler.java
new file mode 100644
index 0000000..b4a88c9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/redis/string/GridRedisStrlenCommandHandler.java
@@ -0,0 +1,83 @@
+/*
+ * 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.handlers.redis.string;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.processors.rest.GridRestProtocolHandler;
+import org.apache.ignite.internal.processors.rest.GridRestResponse;
+import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisRestCommandHandler;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisProtocolParser;
+import org.apache.ignite.internal.processors.rest.request.GridRestCacheRequest;
+import org.apache.ignite.internal.processors.rest.request.GridRestRequest;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import static org.apache.ignite.internal.processors.rest.GridRestCommand.CACHE_GET;
+import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisCommand.STRLEN;
+
+/**
+ * Redis STRLEN command handler.
+ */
+public class GridRedisStrlenCommandHandler extends GridRedisRestCommandHandler {
+    /** Supported commands. */
+    private static final Collection<GridRedisCommand> SUPPORTED_COMMANDS = U.sealList(
+        STRLEN
+    );
+
+    /** {@inheritDoc} */
+    public GridRedisStrlenCommandHandler(final IgniteLogger log, final GridRestProtocolHandler hnd) {
+        super(log, hnd);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<GridRedisCommand> supportedCommands() {
+        return SUPPORTED_COMMANDS;
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridRestRequest asRestRequest(GridRedisMessage msg) throws IgniteCheckedException {
+        assert msg != null;
+
+        GridRestCacheRequest restReq = new GridRestCacheRequest();
+
+        restReq.clientId(msg.clientId());
+        restReq.key(msg.key());
+
+        restReq.command(CACHE_GET);
+
+        return restReq;
+    }
+
+    /** {@inheritDoc} */
+    @Override public ByteBuffer makeResponse(final GridRestResponse restRes, List<String> params) {
+        if (restRes.getResponse() == null)
+            return GridRedisProtocolParser.toInteger("0");
+
+        if (restRes.getResponse() instanceof String) {
+            int len = String.valueOf(restRes.getResponse()).length();
+            return GridRedisProtocolParser.toInteger(String.valueOf(len));
+        }
+        else
+            return GridRedisProtocolParser.toTypeError("Operation against a key holding the wrong kind of value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridClientPacketType.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridClientPacketType.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridClientPacketType.java
index 2ffa2df..d861d67 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridClientPacketType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridClientPacketType.java
@@ -24,6 +24,9 @@ public enum GridClientPacketType {
     /** Memcache protocol message. */
     MEMCACHE,
 
+    /** Redis protocol message. */
+    REDIS,
+
     /** Ignite handshake. */
     IGNITE_HANDSHAKE,
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java
index 2cfdb75..1c1c6dc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java
@@ -42,6 +42,8 @@ import org.apache.ignite.internal.processors.rest.client.message.GridClientRespo
 import org.apache.ignite.internal.processors.rest.client.message.GridClientTaskRequest;
 import org.apache.ignite.internal.processors.rest.client.message.GridClientTopologyRequest;
 import org.apache.ignite.internal.processors.rest.handlers.cache.GridCacheRestMetrics;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisNioListener;
 import org.apache.ignite.internal.processors.rest.request.GridRestCacheRequest;
 import org.apache.ignite.internal.processors.rest.request.GridRestRequest;
 import org.apache.ignite.internal.processors.rest.request.GridRestTaskRequest;
@@ -126,9 +128,12 @@ public class GridTcpRestNioListener extends GridNioServerListenerAdapter<GridCli
     /** Protocol handler. */
     private GridRestProtocolHandler hnd;
 
-    /** Handler for all memcache requests */
+    /** Handler for all memcache requests. */
     private GridTcpMemcachedNioListener memcachedLsnr;
 
+    /** Handler for all Redis requests. */
+    private GridRedisNioListener redisLsnr;
+
     /**
      * Creates listener which will convert incoming tcp packets to rest requests and forward them to
      * a given rest handler.
@@ -141,6 +146,7 @@ public class GridTcpRestNioListener extends GridNioServerListenerAdapter<GridCli
     public GridTcpRestNioListener(IgniteLogger log, GridTcpRestProtocol proto, GridRestProtocolHandler hnd,
         GridKernalContext ctx) {
         memcachedLsnr = new GridTcpMemcachedNioListener(log, hnd, ctx);
+        redisLsnr = new GridRedisNioListener(log, hnd, ctx);
 
         this.log = log;
         this.proto = proto;
@@ -178,6 +184,8 @@ public class GridTcpRestNioListener extends GridNioServerListenerAdapter<GridCli
     @Override public void onMessage(final GridNioSession ses, final GridClientMessage msg) {
         if (msg instanceof GridMemcachedMessage)
             memcachedLsnr.onMessage(ses, (GridMemcachedMessage)msg);
+        else if (msg instanceof GridRedisMessage)
+            redisLsnr.onMessage(ses, (GridRedisMessage)msg);
         else {
             if (msg instanceof GridClientPingPacket)
                 ses.send(msg);
@@ -310,7 +318,7 @@ public class GridTcpRestNioListener extends GridNioServerListenerAdapter<GridCli
             restReq = restCacheReq;
         }
         else if (msg instanceof GridClientTaskRequest) {
-            GridClientTaskRequest req = (GridClientTaskRequest) msg;
+            GridClientTaskRequest req = (GridClientTaskRequest)msg;
 
             GridRestTaskRequest restTaskReq = new GridRestTaskRequest();
 
@@ -322,7 +330,7 @@ public class GridTcpRestNioListener extends GridNioServerListenerAdapter<GridCli
             restReq = restTaskReq;
         }
         else if (msg instanceof GridClientTopologyRequest) {
-            GridClientTopologyRequest req = (GridClientTopologyRequest) msg;
+            GridClientTopologyRequest req = (GridClientTopologyRequest)msg;
 
             GridRestTopologyRequest restTopReq = new GridRestTopologyRequest();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestParser.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestParser.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestParser.java
index de9851c..b4c8b7c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestParser.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestParser.java
@@ -32,6 +32,8 @@ import org.apache.ignite.internal.processors.rest.client.message.GridClientMessa
 import org.apache.ignite.internal.processors.rest.client.message.GridClientPingPacket;
 import org.apache.ignite.internal.processors.rest.client.message.GridRouterRequest;
 import org.apache.ignite.internal.processors.rest.client.message.GridRouterResponse;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage;
+import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisProtocolParser;
 import org.apache.ignite.internal.util.GridByteArrayList;
 import org.apache.ignite.internal.util.GridClientByteUtils;
 import org.apache.ignite.internal.util.nio.GridNioParser;
@@ -58,6 +60,7 @@ import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemca
 import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.MEMCACHE_REQ_FLAG;
 import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.MEMCACHE_RES_FLAG;
 import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.SERIALIZED_FLAG;
+import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage.RESP_REQ_FLAG;
 import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.MARSHALLER;
 import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.PARSER_STATE;
 
@@ -101,6 +104,11 @@ public class GridTcpRestParser implements GridNioParser {
 
                     break;
 
+                case RESP_REQ_FLAG:
+                    state.packetType(GridClientPacketType.REDIS);
+
+                    break;
+
                 case IGNITE_REQ_FLAG:
                     // Skip header.
                     buf.get();
@@ -138,6 +146,11 @@ public class GridTcpRestParser implements GridNioParser {
 
                 break;
 
+            case REDIS:
+                res = GridRedisProtocolParser.readArray(buf);
+
+                break;
+
             case IGNITE_HANDSHAKE:
                 res = parseHandshake(buf, state);
 
@@ -170,6 +183,8 @@ public class GridTcpRestParser implements GridNioParser {
 
         if (msg instanceof GridMemcachedMessage)
             return encodeMemcache((GridMemcachedMessage)msg);
+        else if (msg instanceof GridRedisMessage)
+            return ((GridRedisMessage)msg).getResponse();
         else if (msg instanceof GridClientPingPacket)
             return ByteBuffer.wrap(GridClientPingPacket.PING_PACKET);
         else if (msg instanceof GridClientHandshakeRequest) {
@@ -450,8 +465,8 @@ public class GridTcpRestParser implements GridNioParser {
      * @param intBuf Intermediate buffer to read bytes from and to save remaining bytes to.
      * @param size Number of bytes to read.
      * @return Resulting byte array or {@code null}, if both buffers contain less bytes
-     *         than required. In case of non-null result, the intermediate buffer is empty.
-     *         In case of {@code null} result, the input buffer is empty (read fully).
+     * than required. In case of non-null result, the intermediate buffer is empty.
+     * In case of {@code null} result, the input buffer is empty (read fully).
      * @throws IOException If IO error occurs.
      */
     @Nullable private byte[] statefulRead(ByteBuffer buf, ByteArrayOutputStream intBuf, int size) throws IOException {
@@ -506,7 +521,8 @@ public class GridTcpRestParser implements GridNioParser {
      * @throws IOException On marshaller error.
      * @throws IgniteCheckedException If no marshaller was defined for the session.
      */
-    protected GridClientMessage parseClientMessage(GridNioSession ses, ParserState state) throws IOException, IgniteCheckedException {
+    protected GridClientMessage parseClientMessage(GridNioSession ses,
+        ParserState state) throws IOException, IgniteCheckedException {
         GridClientMessage msg;
 
         if (routerClient) {
@@ -597,8 +613,8 @@ public class GridTcpRestParser implements GridNioParser {
         assert res.size() == HDR_LEN;
 
         if (flagsLen > 0) {
-            res.add((short) keyFlags);
-            res.add((short) valFlags);
+            res.add((short)keyFlags);
+            res.add((short)valFlags);
         }
 
         assert msg.key() == null || msg.key() instanceof byte[];
@@ -622,7 +638,8 @@ public class GridTcpRestParser implements GridNioParser {
      * @throws IOException If parsing failed.
      * @throws IgniteCheckedException If deserialization failed.
      */
-    private GridClientMessage assemble(GridNioSession ses, GridMemcachedMessage req) throws IOException, IgniteCheckedException {
+    private GridClientMessage assemble(GridNioSession ses,
+        GridMemcachedMessage req) throws IOException, IgniteCheckedException {
         byte[] extras = req.extras();
 
         // First, decode key and value, if any
@@ -765,17 +782,17 @@ public class GridTcpRestParser implements GridNioParser {
             flags |= BOOLEAN_FLAG;
         }
         else if (obj instanceof Integer) {
-            data = U.intToBytes((Integer) obj);
+            data = U.intToBytes((Integer)obj);
 
             flags |= INT_FLAG;
         }
         else if (obj instanceof Long) {
-            data = U.longToBytes((Long) obj);
+            data = U.longToBytes((Long)obj);
 
             flags |= LONG_FLAG;
         }
         else if (obj instanceof Date) {
-            data = U.longToBytes(((Date) obj).getTime());
+            data = U.longToBytes(((Date)obj).getTime());
 
             flags |= DATE_FLAG;
         }
@@ -785,12 +802,12 @@ public class GridTcpRestParser implements GridNioParser {
             flags |= BYTE_FLAG;
         }
         else if (obj instanceof Float) {
-            data = U.intToBytes(Float.floatToIntBits((Float) obj));
+            data = U.intToBytes(Float.floatToIntBits((Float)obj));
 
             flags |= FLOAT_FLAG;
         }
         else if (obj instanceof Double) {
-            data = U.longToBytes(Double.doubleToLongBits((Double) obj));
+            data = U.longToBytes(Double.doubleToLongBits((Double)obj));
 
             flags |= DOUBLE_FLAG;
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java
new file mode 100644
index 0000000..8bcabe2
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisCommand.java
@@ -0,0 +1,80 @@
+/*
+ * 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.tcp.redis;
+
+/**
+ * Supported Redis-specific commands.
+ * See <a href="http://redis.io/commands">Redis commands</a> for details.
+ * <p>
+ * Cache operations are handled via REST.
+ */
+public enum GridRedisCommand {
+    // Connections.
+    /** Ping. */
+    PING("PING"),
+    /** Connection close. */
+    QUIT("QUIT"),
+    /** Echo. */
+    ECHO("ECHO"),
+
+    // String commands.
+    /** GET. */
+    GET("GET"),
+    /** MGET. */
+    MGET("MGET"),
+    /** SET. */
+    SET("SET"),
+    /** MSET. */
+    MSET("MSET"),
+    /** INCR. */
+    INCR("INCR"),
+    /** DECR. */
+    DECR("DECR"),
+    /** INCRBY. */
+    INCRBY("INCRBY"),
+    /** DECRBY. */
+    DECRBY("DECRBY"),
+    /** APPEND. */
+    APPEND("APPEND"),
+    /** STRLEN. */
+    STRLEN("STRLEN"),
+    /** GETSET. */
+    GETSET("GETSET"),
+    /** SETRANGE. */
+    SETRANGE("SETRANGE"),
+    /** GETRANGE. */
+    GETRANGE("GETRANGE"),
+
+    // Key commands.
+    /** DEL. */
+    DEL("DEL"),
+    /** EXISTS. */
+    EXISTS("EXISTS"),
+
+    // Server commands.
+    /** DBSIZE. */
+    DBSIZE("DBSIZE");
+
+    /** String for command. */
+    private final String cmd;
+
+    /** Constructor. */
+    GridRedisCommand(String cmd) {
+        this.cmd = cmd;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisMessage.java
new file mode 100644
index 0000000..367d785
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisMessage.java
@@ -0,0 +1,219 @@
+/*
+ * 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.tcp.redis;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.rest.client.message.GridClientMessage;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Message to communicate with Redis client. Contains command, its attributes and response.
+ */
+public class GridRedisMessage implements GridClientMessage {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Random UUID used for REDIS clients authentication. */
+    private static final UUID RESP_ID = UUID.randomUUID();
+
+    /** Request byte. */
+    public static final byte RESP_REQ_FLAG = GridRedisProtocolParser.ARRAY;
+
+    /** Command index. */
+    private static final int CMD_POS = 0;
+
+    /** Key index. */
+    private static final int KEY_POS = 1;
+
+    /** Auxiliary parameters offset. */
+    private static final int AUX_OFFSET = 2;
+
+    /** Request message parts. */
+    private transient final List<String> msgParts;
+
+    /** Response. */
+    private ByteBuffer response;
+
+    /** Cache name. */
+    private String cacheName;
+
+    /**
+     * Constructor.
+     *
+     * @param msgLen Length of the Redis message (command with parameters).
+     */
+    public GridRedisMessage(int msgLen) {
+        msgParts = new ArrayList<>(msgLen);
+    }
+
+    /**
+     * Appends the specified part to the message.
+     *
+     * @param part Part to append.
+     */
+    public void append(String part) {
+        msgParts.add(part);
+    }
+
+    /**
+     * Sets the response.
+     *
+     * @param response Response.
+     */
+    public void setResponse(ByteBuffer response) {
+        this.response = response;
+    }
+
+    /**
+     * Gets the response.
+     *
+     * @return Response.
+     */
+    public ByteBuffer getResponse() {
+        return response;
+    }
+
+    /**
+     * Gets all message parts.
+     *
+     * @return Message elements.
+     */
+    private List<String> getMsgParts() {
+        return msgParts;
+    }
+
+    /**
+     * @return Number of elements in the message.
+     */
+    public int messageSize() {
+        return msgParts.size();
+    }
+
+    /**
+     * @return {@link GridRedisCommand}.
+     */
+    public GridRedisCommand command() {
+        return GridRedisCommand.valueOf(msgParts.get(CMD_POS).toUpperCase());
+    }
+
+    /**
+     * @return Key for the command.
+     */
+    public String key() {
+        if (msgParts.size() <= KEY_POS)
+            return null;
+
+        return msgParts.get(KEY_POS);
+    }
+
+    /**
+     * @return Parameters by index if available.
+     */
+    public String aux(int idx) {
+        if (msgParts.size() <= idx)
+            return null;
+
+        return msgParts.get(idx);
+    }
+
+    /**
+     * @return All parameters if available.
+     */
+    public List<String> aux() {
+        if (msgParts.size() <= AUX_OFFSET)
+            return null;
+
+        return msgParts.subList(AUX_OFFSET, msgParts.size());
+    }
+
+    /**
+     * @return All parameters for multi-key commands if available.
+     */
+    public List<String> auxMKeys() {
+        if (msgParts.size() <= KEY_POS)
+            return null;
+
+        return msgParts.subList(KEY_POS, msgParts.size());
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridRedisMessage.class, this);
+    }
+
+    /**
+     * @return Cache name.
+     */
+    @Nullable public String cacheName() {
+        return cacheName;
+    }
+
+    /**
+     * @param cacheName Cache name.
+     */
+    public void cacheName(String cacheName) {
+        assert cacheName != null;
+
+        this.cacheName = cacheName;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long requestId() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void requestId(long reqId) {
+
+    }
+
+    /** {@inheritDoc} */
+    @Override public UUID clientId() {
+        return RESP_ID;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clientId(UUID id) {
+        throw new IgniteException("Setting client id is not expected!");
+    }
+
+    /** {@inheritDoc} */
+    @Override public UUID destinationId() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void destinationId(UUID id) {
+
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] sessionToken() {
+        return new byte[0];
+    }
+
+    /** {@inheritDoc} */
+    @Override public void sessionToken(byte[] sesTok) {
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java
new file mode 100644
index 0000000..45c8061
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisNioListener.java
@@ -0,0 +1,146 @@
+/*
+ * 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.tcp.redis;
+
+import java.util.EnumMap;
+import java.util.Map;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.rest.GridRestProtocolHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.GridRedisConnectionCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisDelCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.key.GridRedisExistsCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.server.GridRedisDbSizeCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisAppendCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisGetCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisGetRangeCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisGetSetCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisIncrDecrCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisMGetCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisMSetCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisSetCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisSetRangeCommandHandler;
+import org.apache.ignite.internal.processors.rest.handlers.redis.string.GridRedisStrlenCommandHandler;
+import org.apache.ignite.internal.util.nio.GridNioFuture;
+import org.apache.ignite.internal.util.nio.GridNioServerListenerAdapter;
+import org.apache.ignite.internal.util.nio.GridNioSession;
+import org.apache.ignite.internal.util.typedef.CIX1;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Listener for Redis protocol requests.
+ */
+public class GridRedisNioListener extends GridNioServerListenerAdapter<GridRedisMessage> {
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /** Redis-specific handlers. */
+    protected final Map<GridRedisCommand, GridRedisCommandHandler> handlers = new EnumMap<>(GridRedisCommand.class);
+
+    /**
+     * @param log Logger.
+     * @param hnd REST protocol handler.
+     * @param ctx Context.
+     */
+    public GridRedisNioListener(IgniteLogger log, GridRestProtocolHandler hnd, GridKernalContext ctx) {
+        this.log = log;
+
+        // connection commands.
+        addCommandHandler(new GridRedisConnectionCommandHandler());
+
+        // string commands.
+        addCommandHandler(new GridRedisGetCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisSetCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisMSetCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisMGetCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisIncrDecrCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisAppendCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisGetSetCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisStrlenCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisSetRangeCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisGetRangeCommandHandler(log, hnd));
+
+        // key commands.
+        addCommandHandler(new GridRedisDelCommandHandler(log, hnd));
+        addCommandHandler(new GridRedisExistsCommandHandler(log, hnd));
+
+        // server commands.
+        addCommandHandler(new GridRedisDbSizeCommandHandler(log, hnd));
+    }
+
+    /**
+     * Adds Redis-specific command handlers.
+     * <p>
+     * Generic commands are treated by REST.
+     *
+     * @param hnd Redis-specific command handler.
+     */
+    private void addCommandHandler(GridRedisCommandHandler hnd) {
+        assert !handlers.containsValue(hnd);
+
+        if (log.isDebugEnabled())
+            log.debug("Added Redis command handler: " + hnd);
+
+        for (GridRedisCommand cmd : hnd.supportedCommands()) {
+            assert !handlers.containsKey(cmd) : cmd;
+
+            handlers.put(cmd, hnd);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onConnected(GridNioSession ses) {
+        // No-op, never called.
+        assert false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onDisconnected(GridNioSession ses, @Nullable Exception e) {
+        // No-op, never called.
+        assert false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onMessage(final GridNioSession ses, final GridRedisMessage msg) {
+        if (handlers.get(msg.command()) != null) {
+            IgniteInternalFuture<GridRedisMessage> f = handlers.get(msg.command()).handleAsync(msg);
+
+            f.listen(new CIX1<IgniteInternalFuture<GridRedisMessage>>() {
+                @Override public void applyx(IgniteInternalFuture<GridRedisMessage> f) throws IgniteCheckedException {
+                    GridRedisMessage res = f.get();
+
+                    sendResponse(ses, res);
+                }
+            });
+        }
+    }
+
+    /**
+     * Sends a response to be decoded and sent to the Redis client.
+     *
+     * @param ses NIO session.
+     * @param res Response.
+     * @return NIO send future.
+     */
+    private GridNioFuture<?> sendResponse(GridNioSession ses, GridRedisMessage res) {
+        return ses.send(res);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b8a2b8ed/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisProtocolParser.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisProtocolParser.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisProtocolParser.java
new file mode 100644
index 0000000..85fad39
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/redis/GridRedisProtocolParser.java
@@ -0,0 +1,314 @@
+/*
+ * 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.tcp.redis;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.Map;
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ * Parser to decode/encode Redis protocol (RESP) requests.
+ */
+public class GridRedisProtocolParser {
+    /** + prefix. */
+    private static final byte SIMPLE_STRING = 43;
+
+    /** $ */
+    private static final byte BULK_STRING = 36;
+
+    /** : */
+    private static final byte INTEGER = 58;
+
+    /** * */
+    static final byte ARRAY = 42;
+
+    /** - */
+    private static final byte ERROR = 45;
+
+    /** Carriage return code. */
+    private static final byte CR = 13;
+
+    /** Line feed code. */
+    private static final byte LF = 10;
+
+    /** CRLF. */
+    private static final byte[] CRLF = new byte[] {13, 10};
+
+    /** Generic error prefix. */
+    private static final byte[] ERR_GENERIC = "ERR ".getBytes();
+
+    /** Prefix for errors on operations with the wrong type. */
+    private static final byte[] ERR_TYPE = "WRONGTYPE ".getBytes();
+
+    /** Prefix for errors on authentication. */
+    private static final byte[] ERR_AUTH = "NOAUTH ".getBytes();
+
+    /** Null bulk string for nil response. */
+    private static final byte[] NIL = "$-1\r\n".getBytes();
+
+    /** OK response. */
+    private static final byte[] OK = "OK".getBytes();
+
+    /**
+     * Reads an array into {@link GridRedisMessage}.
+     *
+     * @param buf Buffer.
+     * @return {@link GridRedisMessage}.
+     * @throws IgniteCheckedException
+     */
+    public static GridRedisMessage readArray(ByteBuffer buf) throws IgniteCheckedException {
+        byte b = buf.get();
+
+        if (b != ARRAY)
+            throw new IgniteCheckedException("Invalid request byte! " + b);
+
+        int arrLen = elCnt(buf);
+
+        GridRedisMessage msg = new GridRedisMessage(arrLen);
+
+        for (int i = 0; i < arrLen; i++)
+            msg.append(readBulkStr(buf));
+
+        return msg;
+    }
+
+    /**
+     * Reads a bulk string.
+     *
+     * @param buf Buffer.
+     * @return Bulk string.
+     * @throws IgniteCheckedException
+     */
+    public static String readBulkStr(ByteBuffer buf) throws IgniteCheckedException {
+        byte b = buf.get();
+
+        if (b != BULK_STRING)
+            throw new IgniteCheckedException("Invalid bulk string prefix! " + b);
+
+        int len = elCnt(buf);
+        byte[] bulkStr = new byte[len];
+
+        buf.get(bulkStr, 0, len);
+
+        if (buf.get() != CR || buf.get() != LF)
+            throw new IgniteCheckedException("Invalid request syntax!");
+
+        return new String(bulkStr);
+    }
+
+    /**
+     * Counts elements in buffer.
+     *
+     * @param buf Buffer.
+     * @return Count of elements.
+     */
+    private static int elCnt(ByteBuffer buf) throws IgniteCheckedException {
+        byte[] arrLen = new byte[9];
+
+        int idx = 0;
+        byte b = buf.get();
+        while (b != CR) {
+            arrLen[idx++] = b;
+            b = buf.get();
+        }
+
+        if (buf.get() != LF)
+            throw new IgniteCheckedException("Invalid request syntax!");
+
+        return Integer.parseInt(new String(arrLen, 0, idx));
+    }
+
+    /**
+     * Converts a simple string data to a {@link ByteBuffer}.
+     *
+     * @param val String to be converted to a simple string.
+     * @return Redis simple string.
+     */
+    public static ByteBuffer toSimpleString(String val) {
+        byte[] b = val.getBytes();
+
+        return toSimpleString(b);
+    }
+
+    /**
+     * Creates a simple string data as a {@link ByteBuffer}.
+     *
+     * @param b Bytes for a simple string.
+     * @return Redis simple string.
+     */
+    public static ByteBuffer toSimpleString(byte[] b) {
+        ByteBuffer buf = ByteBuffer.allocate(b.length + 3);
+        buf.put(SIMPLE_STRING);
+        buf.put(b);
+        buf.put(CRLF);
+
+        buf.flip();
+
+        return buf;
+    }
+
+    /**
+     * @return Standard OK string.
+     */
+    public static ByteBuffer oKString() {
+        return toSimpleString(OK);
+    }
+
+    /**
+     * Creates a generic error response.
+     *
+     * @param errMsg Error message.
+     * @return Error response.
+     */
+    public static ByteBuffer toGenericError(String errMsg) {
+        return toError(errMsg, ERR_GENERIC);
+    }
+
+    /**
+     * Creates an error response on operation against the wrong data type.
+     *
+     * @param errMsg Error message.
+     * @return Error response.
+     */
+    public static ByteBuffer toTypeError(String errMsg) {
+        return toError(errMsg, ERR_TYPE);
+    }
+
+    /**
+     * Creates an error response.
+     *
+     * @param errMsg Error message.
+     * @param errPrefix Error prefix.
+     * @return Error response.
+     */
+    private static ByteBuffer toError(String errMsg, byte[] errPrefix) {
+        byte[] b = errMsg.getBytes();
+
+        ByteBuffer buf = ByteBuffer.allocate(b.length + errPrefix.length + 3);
+        buf.put(ERROR);
+        buf.put(errPrefix);
+        buf.put(b);
+        buf.put(CRLF);
+
+        buf.flip();
+
+        return buf;
+    }
+
+    /**
+     * Converts an integer result to a RESP integer.
+     *
+     * @param integer Integer result.
+     * @return REDIS integer.
+     */
+    public static ByteBuffer toInteger(String integer) {
+        byte[] b = integer.getBytes();
+
+        ByteBuffer buf = ByteBuffer.allocate(b.length + 3);
+        buf.put(INTEGER);
+        buf.put(b);
+        buf.put(CRLF);
+
+        buf.flip();
+
+        return buf;
+    }
+
+    /**
+     * Converts an integer result to a RESP integer.
+     *
+     * @param integer Integer result.
+     * @return REDIS integer.
+     */
+    public static ByteBuffer toInteger(int integer) {
+        return toInteger(String.valueOf(integer));
+    }
+
+    /**
+     * Creates Nil response.
+     *
+     * @return Nil response.
+     */
+    public static ByteBuffer nil() {
+        ByteBuffer buf = ByteBuffer.allocate(NIL.length);
+        buf.put(NIL);
+
+        buf.flip();
+
+        return buf;
+    }
+
+    /**
+     * Converts a resultant object to a bulk string.
+     *
+     * @param val Object.
+     * @return Bulk string.
+     */
+    public static ByteBuffer toBulkString(Object val) {
+        assert val != null;
+
+        byte[] b = String.valueOf(val).getBytes();
+        byte[] l = String.valueOf(b.length).getBytes();
+
+        ByteBuffer buf = ByteBuffer.allocate(b.length + l.length + 5);
+        buf.put(BULK_STRING);
+        buf.put(l);
+        buf.put(CRLF);
+        buf.put(b);
+        buf.put(CRLF);
+
+        buf.flip();
+
+        return buf;
+    }
+
+    /**
+     * Converts a resultant map response to an array.
+     *
+     * @param vals Map.
+     * @return Array response.
+     */
+    public static ByteBuffer toArray(Map<Object, Object> vals) {
+        return toArray(vals.values());
+    }
+
+    /**
+     * Converts a resultant collection response to an array.
+     *
+     * @param vals Array elements.
+     * @return Array response.
+     */
+    public static ByteBuffer toArray(Collection<Object> vals) {
+        assert vals != null;
+
+        byte[] arrSize = String.valueOf(vals.size()).getBytes();
+
+        ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 1024);
+        buf.put(ARRAY);
+        buf.put(arrSize);
+        buf.put(CRLF);
+
+        for (Object val : vals)
+            buf.put(toBulkString(val));
+
+        buf.flip();
+
+        return buf;
+    }
+}


Mime
View raw message