geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From u..@apache.org
Subject [04/21] geode git commit: GEODE-2449: Moved Redis out of core with minimal Extension work added
Date Fri, 10 Feb 2017 21:34:07 GMT
http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java
new file mode 100644
index 0000000..d63c02f
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java
@@ -0,0 +1,96 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+import org.apache.geode.redis.internal.RedisDataType;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class GetRangeExecutor extends StringExecutor {
+
+  private final String ERROR_NOT_INT = "The indexes provided must be numeric values";
+
+  private final int startIndex = 2;
+
+  private final int stopIndex = 3;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 4) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.GETRANGE));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkDataType(key, RedisDataType.REDIS_STRING, context);
+    ByteArrayWrapper valueWrapper = r.get(key);
+
+    if (valueWrapper == null) {
+      command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+      return;
+    }
+
+    byte[] value = valueWrapper.toBytes();
+    int length = value.length;
+
+    long start;
+    long end;
+
+
+    try {
+      byte[] startI = commandElems.get(startIndex);
+      byte[] stopI = commandElems.get(stopIndex);
+      start = Coder.bytesToLong(startI);
+      end = Coder.bytesToLong(stopI);
+    } catch (NumberFormatException e) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT));
+      return;
+    }
+    start = getBoundedStartIndex(start, length);
+    end = getBoundedEndIndex(end, length);
+
+    /*
+     * If the properly formatted indexes are illegal, send nil
+     */
+    if (start > end || start == length) {
+      command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+      return;
+    }
+    /*
+     * 1 is added to end because the end in copyOfRange is exclusive but in Redis it is inclusive
+     */
+    if (end != length)
+      end++;
+    byte[] returnRange = Arrays.copyOfRange(value, (int) start, (int) end);
+    if (returnRange == null || returnRange.length == 0) {
+      command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+      return;
+    }
+
+    command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), returnRange));
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java
new file mode 100644
index 0000000..b0edfd4
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java
@@ -0,0 +1,58 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.List;
+
+public class GetSetExecutor extends StringExecutor {
+
+  private final int VALUE_INDEX = 2;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 3) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.GETSET));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkAndSetDataType(key, context);
+
+    byte[] newCharValue = commandElems.get(VALUE_INDEX);
+    ByteArrayWrapper newValueWrapper = new ByteArrayWrapper(newCharValue);
+
+    ByteArrayWrapper oldValueWrapper = r.get(key);
+    r.put(key, newValueWrapper);
+
+    if (oldValueWrapper == null) {
+      command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+    } else {
+      command.setResponse(
+          Coder.getBulkStringResponse(context.getByteBufAllocator(), oldValueWrapper.toBytes()));
+    }
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByExecutor.java
new file mode 100644
index 0000000..0dac15b
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByExecutor.java
@@ -0,0 +1,108 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.List;
+
+public class IncrByExecutor extends StringExecutor {
+
+  private final String ERROR_VALUE_NOT_USABLE =
+      "The value at this key cannot be incremented numerically";
+
+  private final String ERROR_INCREMENT_NOT_USABLE = "The increment on this key must be numeric";
+
+  private final String ERROR_OVERFLOW = "This incrementation cannot be performed due to overflow";
+
+  private final int INCREMENT_INDEX = 2;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 3) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.INCRBY));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkAndSetDataType(key, context);
+    ByteArrayWrapper valueWrapper = r.get(key);
+
+    /*
+     * Try increment
+     */
+
+    byte[] incrArray = commandElems.get(INCREMENT_INDEX);
+    Long increment;
+
+    try {
+      increment = Coder.bytesToLong(incrArray);
+    } catch (NumberFormatException e) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_INCREMENT_NOT_USABLE));
+      return;
+    }
+
+    /*
+     * Value does not exist
+     */
+
+    if (valueWrapper == null) {
+      r.put(key, new ByteArrayWrapper(incrArray));
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), increment));
+      return;
+    }
+
+    /*
+     * Value exists
+     */
+
+    String stringValue = Coder.bytesToString(valueWrapper.toBytes());
+    Long value;
+    try {
+      value = Long.parseLong(stringValue);
+    } catch (NumberFormatException e) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_VALUE_NOT_USABLE));
+      return;
+    }
+
+    /*
+     * Check for overflow
+     */
+    if (value >= 0 && increment > (Long.MAX_VALUE - value)) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_OVERFLOW));
+      return;
+    }
+
+    value += increment;
+
+    stringValue = "" + value;
+    r.put(key, new ByteArrayWrapper(Coder.stringToBytes(stringValue)));
+
+    command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), value));
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java
new file mode 100644
index 0000000..e1ff80f
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java
@@ -0,0 +1,128 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.List;
+
+public class IncrByFloatExecutor extends StringExecutor {
+
+  private final String ERROR_VALUE_NOT_USABLE =
+      "Invalid value at this key and cannot be incremented numerically";
+
+  private final String ERROR_INCREMENT_NOT_USABLE =
+      "The increment on this key must be a valid floating point numeric";
+
+  private final String ERROR_OVERFLOW = "This incrementation cannot be performed due to overflow";
+
+  private final int INCREMENT_INDEX = 2;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+    if (commandElems.size() < 3) {
+      command
+          .setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.INCRBYFLOAT));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkAndSetDataType(key, context);
+    ByteArrayWrapper valueWrapper = r.get(key);
+
+    /*
+     * Try increment
+     */
+
+    byte[] incrArray = commandElems.get(INCREMENT_INDEX);
+    String doub = Coder.bytesToString(incrArray).toLowerCase();
+    if (doub.contains("inf") || doub.contains("nan")) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+          "Increment would produce NaN or infinity"));
+      return;
+    } else if (valueWrapper != null && valueWrapper.toString().contains(" ")) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_VALUE_NOT_USABLE));
+      return;
+    }
+
+
+    Double increment;
+
+    try {
+      increment = Coder.stringToDouble(doub);
+    } catch (NumberFormatException e) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_INCREMENT_NOT_USABLE));
+      return;
+    }
+
+    /*
+     * Value does not exist
+     */
+
+    if (valueWrapper == null) {
+      r.put(key, new ByteArrayWrapper(incrArray));
+      command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), increment));
+      return;
+    }
+
+    /*
+     * Value exists
+     */
+
+    String stringValue = Coder.bytesToString(valueWrapper.toBytes());
+
+    Double value;
+    try {
+      value = Coder.stringToDouble(stringValue);
+    } catch (NumberFormatException e) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_VALUE_NOT_USABLE));
+      return;
+    }
+
+    /*
+     * Check for overflow
+     */
+    if (value >= 0 && increment > (Double.MAX_VALUE - value)) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_OVERFLOW));
+      return;
+    }
+
+    double result = value + increment;
+    if (Double.isNaN(result) || Double.isInfinite(result)) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ERROR_NAN_INF_INCR));
+      return;
+    }
+    value += increment;
+
+    stringValue = "" + value;
+    r.put(key, new ByteArrayWrapper(Coder.stringToBytes(stringValue)));
+
+    command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), value));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrExecutor.java
new file mode 100644
index 0000000..1e915b2
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/IncrExecutor.java
@@ -0,0 +1,91 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.List;
+
+public class IncrExecutor extends StringExecutor {
+
+  private final String ERROR_VALUE_NOT_USABLE =
+      "The value at this key cannot be incremented numerically";
+
+  private final String ERROR_OVERFLOW = "This incrementation cannot be performed due to overflow";
+
+  private final int INIT_VALUE_INT = 1;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 2) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.INCR));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkAndSetDataType(key, context);
+    ByteArrayWrapper valueWrapper = r.get(key);
+
+    /*
+     * Value does not exist
+     */
+
+    if (valueWrapper == null) {
+      byte[] newValue = {Coder.NUMBER_1_BYTE};
+      r.put(key, new ByteArrayWrapper(newValue));
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), INIT_VALUE_INT));
+      return;
+    }
+
+    /*
+     * Value exists
+     */
+
+    String stringValue = valueWrapper.toString();
+
+    Long value;
+    try {
+      value = Long.parseLong(stringValue);
+    } catch (NumberFormatException e) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_VALUE_NOT_USABLE));
+      return;
+    }
+
+    if (value == Long.MAX_VALUE) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_OVERFLOW));
+      return;
+    }
+
+    value++;
+
+    stringValue = "" + value;
+    r.put(key, new ByteArrayWrapper(Coder.stringToBytes(stringValue)));
+
+
+    command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), value));
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java
new file mode 100644
index 0000000..dab7eae
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java
@@ -0,0 +1,67 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+public class MGetExecutor extends StringExecutor {
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 2) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.MGET));
+      return;
+    }
+
+    Collection<ByteArrayWrapper> keys = new ArrayList<ByteArrayWrapper>();
+    for (int i = 1; i < commandElems.size(); i++) {
+      byte[] keyArray = commandElems.get(i);
+      ByteArrayWrapper key = new ByteArrayWrapper(keyArray);
+      /*
+       * try { checkDataType(key, RedisDataType.REDIS_STRING, context); } catch
+       * (RedisDataTypeMismatchException e) { keys.ad continue; }
+       */
+      keys.add(key);
+    }
+
+    Map<ByteArrayWrapper, ByteArrayWrapper> results = r.getAll(keys);
+
+    Collection<ByteArrayWrapper> values = new ArrayList<ByteArrayWrapper>();
+
+    /*
+     * This is done to preserve order in the output
+     */
+    for (ByteArrayWrapper key : keys)
+      values.add(results.get(key));
+
+    command.setResponse(Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), values));
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java
new file mode 100644
index 0000000..bf06497
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java
@@ -0,0 +1,62 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+import org.apache.geode.redis.internal.RedisDataTypeMismatchException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MSetExecutor extends StringExecutor {
+
+  private final String SUCCESS = "OK";
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 3 || commandElems.size() % 2 == 0) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.MSET));
+      return;
+    }
+
+    Map<ByteArrayWrapper, ByteArrayWrapper> map = new HashMap<ByteArrayWrapper, ByteArrayWrapper>();
+    for (int i = 1; i < commandElems.size(); i += 2) {
+      byte[] keyArray = commandElems.get(i);
+      ByteArrayWrapper key = new ByteArrayWrapper(keyArray);
+      try {
+        checkAndSetDataType(key, context);
+      } catch (RedisDataTypeMismatchException e) {
+        continue;
+      }
+      byte[] value = commandElems.get(i + 1);
+      map.put(key, new ByteArrayWrapper(value));
+    }
+    r.putAll(map);
+
+    command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), SUCCESS));
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetNXExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetNXExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetNXExecutor.java
new file mode 100644
index 0000000..eefffe9
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/MSetNXExecutor.java
@@ -0,0 +1,86 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+import org.apache.geode.redis.internal.RedisDataTypeMismatchException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MSetNXExecutor extends StringExecutor {
+
+  private final int SET = 1;
+
+  private final int NOT_SET = 0;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 3 || commandElems.size() % 2 == 0) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.MSETNX));
+      return;
+    }
+
+    boolean hasEntry = false;
+
+    Map<ByteArrayWrapper, ByteArrayWrapper> map = new HashMap<ByteArrayWrapper, ByteArrayWrapper>();
+    for (int i = 1; i < commandElems.size(); i += 2) {
+      byte[] keyArray = commandElems.get(i);
+      ByteArrayWrapper key = new ByteArrayWrapper(keyArray);
+      try {
+        checkDataType(key, context);
+      } catch (RedisDataTypeMismatchException e) {
+        hasEntry = true;
+        break;
+      }
+      byte[] value = commandElems.get(i + 1);
+      map.put(key, new ByteArrayWrapper(value));
+      if (r.containsKey(key)) {
+        hasEntry = true;
+        break;
+      }
+    }
+    boolean successful = false;
+    if (!hasEntry) {
+      successful = true;
+      for (ByteArrayWrapper k : map.keySet()) {
+        try {
+          checkAndSetDataType(k, context);
+        } catch (RedisDataTypeMismatchException e) {
+          successful = false;
+          break;
+        }
+      }
+      r.putAll(map);
+    }
+    if (successful) {
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), SET));
+    } else {
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), NOT_SET));
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/PSetEXExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/PSetEXExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/PSetEXExecutor.java
new file mode 100644
index 0000000..c8e6111
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/PSetEXExecutor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+
+public class PSetEXExecutor extends SetEXExecutor {
+
+  @Override
+  public boolean timeUnitMillis() {
+    return true;
+  }
+
+  @Override
+  public String getArgsError() {
+    return ArityDef.PSETEX;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetBitExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetBitExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetBitExecutor.java
new file mode 100644
index 0000000..7cef3e0
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetBitExecutor.java
@@ -0,0 +1,108 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.List;
+
+public class SetBitExecutor extends StringExecutor {
+
+  private final String ERROR_NOT_INT = "The number provided must be numeric";
+
+  private final String ERROR_VALUE = "The value is out of range, must be 0 or 1";
+
+  private final String ERROR_ILLEGAL_OFFSET =
+      "The offset is out of range, must be greater than or equal to 0  and at most 4294967295 (512MB)";
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 4) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.SETBIT));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkAndSetDataType(key, context);
+    ByteArrayWrapper wrapper = r.get(key);
+
+    long offset;
+    int value;
+    int returnBit = 0;
+    try {
+      byte[] offAr = commandElems.get(2);
+      byte[] valAr = commandElems.get(3);
+      offset = Coder.bytesToLong(offAr);
+      value = Coder.bytesToInt(valAr);
+    } catch (NumberFormatException e) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT));
+      return;
+    }
+
+    if (value != 0 && value != 1) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_VALUE));
+      return;
+    }
+
+    if (offset < 0 || offset > 4294967295L) {
+      command
+          .setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_ILLEGAL_OFFSET));
+      return;
+    }
+
+    int byteIndex = (int) (offset / 8);
+    offset %= 8;
+
+    if (wrapper == null) {
+      byte[] bytes = new byte[byteIndex + 1];
+      if (value == 1)
+        bytes[byteIndex] = (byte) (0x80 >> offset);
+      r.put(key, new ByteArrayWrapper(bytes));
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0));
+    } else {
+
+      byte[] bytes = wrapper.toBytes();
+      if (byteIndex < bytes.length)
+        returnBit = (bytes[byteIndex] & (0x80 >> offset)) >> (7 - offset);
+      else
+        returnBit = 0;
+
+      if (byteIndex < bytes.length) {
+        bytes[byteIndex] = value == 1 ? (byte) (bytes[byteIndex] | (0x80 >> offset))
+            : (byte) (bytes[byteIndex] & ~(0x80 >> offset));
+        r.put(key, new ByteArrayWrapper(bytes));
+      } else {
+        byte[] newBytes = new byte[byteIndex + 1];
+        System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
+        newBytes[byteIndex] = value == 1 ? (byte) (newBytes[byteIndex] | (0x80 >> offset))
+            : (byte) (newBytes[byteIndex] & ~(0x80 >> offset));
+        r.put(key, new ByteArrayWrapper(newBytes));
+      }
+
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), returnBit));
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetEXExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetEXExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetEXExecutor.java
new file mode 100644
index 0000000..71498c6
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetEXExecutor.java
@@ -0,0 +1,90 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.Extendable;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+import org.apache.geode.redis.internal.executor.AbstractExecutor;
+
+import java.util.List;
+
+public class SetEXExecutor extends StringExecutor implements Extendable {
+
+  private final String ERROR_SECONDS_NOT_A_NUMBER =
+      "The expiration argument provided was not a number";
+
+  private final String ERROR_SECONDS_NOT_LEGAL = "The expiration argument must be greater than 0";
+
+  private final String SUCCESS = "OK";
+
+  private final int VALUE_INDEX = 3;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 4) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), getArgsError()));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    byte[] value = commandElems.get(VALUE_INDEX);
+
+    byte[] expirationArray = commandElems.get(2);
+    long expiration;
+    try {
+      expiration = Coder.bytesToLong(expirationArray);
+    } catch (NumberFormatException e) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_SECONDS_NOT_A_NUMBER));
+      return;
+    }
+
+    if (expiration <= 0) {
+      command.setResponse(
+          Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_SECONDS_NOT_LEGAL));
+      return;
+    }
+
+    if (!timeUnitMillis())
+      expiration *= AbstractExecutor.millisInSecond;
+
+    checkAndSetDataType(key, context);
+    r.put(key, new ByteArrayWrapper(value));
+
+    context.getRegionProvider().setExpiration(key, expiration);
+
+    command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), SUCCESS));
+
+  }
+
+  protected boolean timeUnitMillis() {
+    return false;
+  }
+
+  @Override
+  public String getArgsError() {
+    return ArityDef.SETEX;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetExecutor.java
new file mode 100644
index 0000000..b83e4c1
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetExecutor.java
@@ -0,0 +1,155 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+import org.apache.geode.redis.internal.executor.AbstractExecutor;
+
+import java.util.List;
+
+public class SetExecutor extends StringExecutor {
+
+  private final String SUCCESS = "OK";
+
+  private final int VALUE_INDEX = 2;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 3) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.SET));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkDataType(key, context);
+    byte[] value = commandElems.get(VALUE_INDEX);
+    ByteArrayWrapper valueWrapper = new ByteArrayWrapper(value);
+
+    boolean NX = false; // Set only if not exists
+    boolean XX = false; // Set only if exists
+    long expiration = 0L;
+
+    if (commandElems.size() >= 6) {
+      String elem4;
+      String elem5;
+      String elem6;
+
+      elem4 = Coder.bytesToString(commandElems.get(3));
+      elem5 = Coder.bytesToString(commandElems.get(4));
+      elem6 = Coder.bytesToString(commandElems.get(5));
+
+      if (elem4.equalsIgnoreCase("XX") || elem6.equalsIgnoreCase("XX"))
+        XX = true;
+      else if (elem4.equalsIgnoreCase("NX") || elem6.equalsIgnoreCase("NX"))
+        NX = true;
+
+      if (elem4.equalsIgnoreCase("PX"))
+        expiration = getExpirationMillis(elem4, elem5);
+      else if (elem5.equalsIgnoreCase("PX"))
+        expiration = getExpirationMillis(elem5, elem6);
+      else if (elem4.equalsIgnoreCase("EX"))
+        expiration = getExpirationMillis(elem4, elem5);
+      else if (elem5.equalsIgnoreCase("EX"))
+        expiration = getExpirationMillis(elem5, elem6);
+
+    } else if (commandElems.size() >= 5) {
+      String elem4;
+      String expiry;
+
+      elem4 = Coder.bytesToString(commandElems.get(3));
+      expiry = Coder.bytesToString(commandElems.get(4));
+
+      expiration = getExpirationMillis(elem4, expiry);
+    } else if (commandElems.size() >= 4) {
+      byte[] elem4 = commandElems.get(3);
+      if (elem4.length == 2 && Character.toUpperCase(elem4[1]) == 'X') {
+        if (Character.toUpperCase(elem4[0]) == 'N')
+          NX = true;
+        else if (Character.toUpperCase(elem4[0]) == 'X')
+          XX = true;
+      }
+    }
+
+    boolean keyWasSet = false;
+
+    if (NX)
+      keyWasSet = setNX(r, command, key, valueWrapper, context);
+    else if (XX)
+      keyWasSet = setXX(r, command, key, valueWrapper, context);
+    else {
+      checkAndSetDataType(key, context);
+      r.put(key, valueWrapper);
+      command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), SUCCESS));
+      keyWasSet = true;
+    }
+
+    if (keyWasSet && expiration > 0L) {
+      context.getRegionProvider().setExpiration(key, expiration);
+    }
+
+  }
+
+  private boolean setNX(Region<ByteArrayWrapper, ByteArrayWrapper> r, Command command,
+      ByteArrayWrapper key, ByteArrayWrapper valueWrapper, ExecutionHandlerContext context) {
+    checkAndSetDataType(key, context);
+    Object oldValue = r.putIfAbsent(key, valueWrapper);
+    if (oldValue != null) {
+      command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+      return false;
+    } else {
+      command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), SUCCESS));
+      return true;
+    }
+  }
+
+  private boolean setXX(Region<ByteArrayWrapper, ByteArrayWrapper> r, Command command,
+      ByteArrayWrapper key, ByteArrayWrapper valueWrapper, ExecutionHandlerContext context) {
+    if (r.containsKey(key)) {
+      checkAndSetDataType(key, context);
+      r.put(key, valueWrapper);
+      command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), SUCCESS));
+      return true;
+    } else {
+      command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+      return false;
+    }
+  }
+
+  private long getExpirationMillis(String expx, String expirationString) {
+    long expiration = 0L;
+    try {
+      expiration = Long.parseLong(expirationString);
+    } catch (NumberFormatException e) {
+      return 0L;
+    }
+
+    if (expx.equalsIgnoreCase("EX"))
+      return expiration * AbstractExecutor.millisInSecond;
+    else if (expx.equalsIgnoreCase("PX"))
+      return expiration;
+    else
+      return 0L;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetNXExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetNXExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetNXExecutor.java
new file mode 100644
index 0000000..eb4c75b
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetNXExecutor.java
@@ -0,0 +1,58 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.List;
+
+public class SetNXExecutor extends StringExecutor {
+
+  private final int SET = 1;
+
+  private final int NOT_SET = 0;
+
+  private final int VALUE_INDEX = 2;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 3) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.SETNX));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkAndSetDataType(key, context);
+    byte[] value = commandElems.get(VALUE_INDEX);
+
+    Object oldValue = r.putIfAbsent(key, new ByteArrayWrapper(value));
+
+    if (oldValue != null)
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), NOT_SET));
+    else
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), SET));
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetRangeExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetRangeExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetRangeExecutor.java
new file mode 100644
index 0000000..e1545d0
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/SetRangeExecutor.java
@@ -0,0 +1,96 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+
+import java.util.List;
+
+public class SetRangeExecutor extends StringExecutor {
+
+  private final String ERROR_NOT_INT = "The number provided must be numeric";
+
+  private final String ERROR_ILLEGAL_OFFSET =
+      "The offset is out of range, must be greater than or equal to 0 and the offset added to the length of the value must be less than 536870911 (512MB), the maximum allowed size";
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 4) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.SETRANGE));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkAndSetDataType(key, context);
+    ByteArrayWrapper wrapper = r.get(key);
+
+    int offset;
+    byte[] value = commandElems.get(3);
+    try {
+      byte[] offAr = commandElems.get(2);
+      offset = Coder.bytesToInt(offAr);
+    } catch (NumberFormatException e) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_NOT_INT));
+      return;
+    }
+
+    int totalLength = offset + value.length;
+    if (offset < 0 || totalLength > 536870911) {
+      command
+          .setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_ILLEGAL_OFFSET));
+      return;
+    } else if (value.length == 0) {
+      int length = wrapper == null ? 0 : wrapper.toBytes().length;
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), length));
+      if (wrapper == null)
+        context.getRegionProvider().removeKey(key);
+      return;
+    }
+
+    if (wrapper == null) {
+      byte[] bytes = new byte[totalLength];
+      System.arraycopy(value, 0, bytes, offset, value.length);
+      r.put(key, new ByteArrayWrapper(bytes));
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), bytes.length));
+    } else {
+
+      byte[] bytes = wrapper.toBytes();
+      int returnLength;
+      if (totalLength < bytes.length) {
+        System.arraycopy(value, 0, bytes, offset, value.length);
+        r.put(key, new ByteArrayWrapper(bytes));
+        returnLength = bytes.length;
+      } else {
+        byte[] newBytes = new byte[totalLength];
+        System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
+        System.arraycopy(value, 0, newBytes, offset, value.length);
+        returnLength = newBytes.length;
+        r.put(key, new ByteArrayWrapper(newBytes));
+      }
+
+      command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), returnLength));
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StringExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StringExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StringExecutor.java
new file mode 100644
index 0000000..fb91f09
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StringExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisDataType;
+import org.apache.geode.redis.internal.RedisDataTypeMismatchException;
+import org.apache.geode.redis.internal.executor.AbstractExecutor;
+
+public abstract class StringExecutor extends AbstractExecutor {
+
+  protected final void checkAndSetDataType(ByteArrayWrapper key, ExecutionHandlerContext context) {
+    Object oldVal = context.getRegionProvider().metaPutIfAbsent(key, RedisDataType.REDIS_STRING);
+    if (oldVal == RedisDataType.REDIS_PROTECTED)
+      throw new RedisDataTypeMismatchException("The key name \"" + key + "\" is protected");
+    if (oldVal != null && oldVal != RedisDataType.REDIS_STRING)
+      throw new RedisDataTypeMismatchException(
+          "The key name \"" + key + "\" is already used by a " + oldVal.toString());
+  }
+
+  protected void checkDataType(ByteArrayWrapper key, ExecutionHandlerContext context) {
+    RedisDataType currentType = context.getRegionProvider().getRedisDataType(key);
+    if (currentType == null)
+      return;
+    if (currentType == RedisDataType.REDIS_PROTECTED)
+      throw new RedisDataTypeMismatchException("The key name \"" + key + "\" is protected");
+    if (currentType != RedisDataType.REDIS_STRING)
+      throw new RedisDataTypeMismatchException(
+          "The key name \"" + key + "\" is already used by a " + currentType.toString());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StrlenExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StrlenExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StrlenExecutor.java
new file mode 100644
index 0000000..345db20
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/string/StrlenExecutor.java
@@ -0,0 +1,56 @@
+/*
+ * 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.geode.redis.internal.executor.string;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+import org.apache.geode.redis.internal.RedisDataType;
+
+import java.util.List;
+
+public class StrlenExecutor extends StringExecutor {
+
+  private final int KEY_DOES_NOT_EXIST = 0;
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    List<byte[]> commandElems = command.getProcessedCommand();
+
+    Region<ByteArrayWrapper, ByteArrayWrapper> r = context.getRegionProvider().getStringsRegion();
+
+    if (commandElems.size() < 2) {
+      command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.STRLEN));
+      return;
+    }
+
+    ByteArrayWrapper key = command.getKey();
+    checkDataType(key, RedisDataType.REDIS_STRING, context);
+    ByteArrayWrapper valueWrapper = r.get(key);
+
+
+    if (valueWrapper == null)
+      command
+          .setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), KEY_DOES_NOT_EXIST));
+    else
+      command.setResponse(
+          Coder.getIntegerResponse(context.getByteBufAllocator(), valueWrapper.toBytes().length));
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/DiscardExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/DiscardExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/DiscardExecutor.java
new file mode 100644
index 0000000..16bf575
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/DiscardExecutor.java
@@ -0,0 +1,40 @@
+/*
+ * 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.geode.redis.internal.executor.transactions;
+
+import org.apache.geode.cache.CacheTransactionManager;
+import org.apache.geode.cache.TransactionId;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+
+public class DiscardExecutor extends TransactionExecutor {
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+
+    CacheTransactionManager txm = context.getCacheTransactionManager();
+
+    if (context.hasTransaction()) {
+      TransactionId transactionId = context.getTransactionID();
+      txm.resume(transactionId);
+      txm.rollback();
+      context.clearTransaction();
+    }
+
+    command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), "OK"));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/ExecExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/ExecExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/ExecExecutor.java
new file mode 100644
index 0000000..9f3b402
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/ExecExecutor.java
@@ -0,0 +1,86 @@
+/*
+ * 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.geode.redis.internal.executor.transactions;
+
+import io.netty.buffer.ByteBuf;
+import org.apache.geode.cache.CacheTransactionManager;
+import org.apache.geode.cache.CommitConflictException;
+import org.apache.geode.cache.TransactionId;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants;
+
+import java.util.Queue;
+
+public class ExecExecutor extends TransactionExecutor {
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+
+    CacheTransactionManager txm = context.getCacheTransactionManager();
+
+    if (!context.hasTransaction()) {
+      command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+      return;
+    }
+
+    TransactionId transactionId = context.getTransactionID();
+
+    txm.resume(transactionId);
+
+    boolean hasError = hasError(context.getTransactionQueue());
+
+    if (hasError)
+      txm.rollback();
+    else {
+      try {
+        txm.commit();
+      } catch (CommitConflictException e) {
+        command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+            RedisConstants.ERROR_COMMIT_CONFLICT));
+        context.clearTransaction();
+        return;
+      }
+    }
+
+    ByteBuf response = constructResponseExec(context);
+    command.setResponse(response);
+
+    context.clearTransaction();
+  }
+
+  private ByteBuf constructResponseExec(ExecutionHandlerContext context) {
+    Queue<Command> cQ = context.getTransactionQueue();
+    ByteBuf response = context.getByteBufAllocator().buffer();
+    response.writeByte(Coder.ARRAY_ID);
+    response.writeBytes(Coder.intToBytes(cQ.size()));
+    response.writeBytes(Coder.CRLFar);
+
+    for (Command c : cQ) {
+      ByteBuf r = c.getResponse();
+      response.writeBytes(r);
+    }
+    return response;
+  }
+
+  private boolean hasError(Queue<Command> queue) {
+    for (Command c : queue) {
+      if (c.hasError())
+        return true;
+    }
+    return false;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/MultiExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/MultiExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/MultiExecutor.java
new file mode 100644
index 0000000..21a41d3
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/MultiExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.geode.redis.internal.executor.transactions;
+
+import org.apache.geode.cache.CacheTransactionManager;
+import org.apache.geode.cache.TransactionId;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants;
+
+public class MultiExecutor extends TransactionExecutor {
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+
+    CacheTransactionManager txm = context.getCacheTransactionManager();
+
+    command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), "OK"));
+
+    if (context.hasTransaction()) {
+      throw new IllegalStateException(RedisConstants.ERROR_NESTED_MULTI);
+    }
+
+    txm.begin();
+
+    TransactionId id = txm.suspend();
+
+    context.setTransactionID(id);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/TransactionExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/TransactionExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/TransactionExecutor.java
new file mode 100644
index 0000000..27ade8f
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/TransactionExecutor.java
@@ -0,0 +1,21 @@
+/*
+ * 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.geode.redis.internal.executor.transactions;
+
+import org.apache.geode.redis.internal.executor.AbstractExecutor;
+
+public abstract class TransactionExecutor extends AbstractExecutor {
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/UnwatchExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/UnwatchExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/UnwatchExecutor.java
new file mode 100644
index 0000000..2323550
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/UnwatchExecutor.java
@@ -0,0 +1,30 @@
+/*
+ * 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.geode.redis.internal.executor.transactions;
+
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants;
+
+public class UnwatchExecutor extends TransactionExecutor {
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    command.setResponse(
+        Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ERROR_UNWATCH));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/WatchExecutor.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/WatchExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/WatchExecutor.java
new file mode 100644
index 0000000..1dbe36e
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/executor/transactions/WatchExecutor.java
@@ -0,0 +1,30 @@
+/*
+ * 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.geode.redis.internal.executor.transactions;
+
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.RedisConstants;
+
+public class WatchExecutor extends TransactionExecutor {
+
+  @Override
+  public void executeCommand(Command command, ExecutionHandlerContext context) {
+    command.setResponse(
+        Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ERROR_WATCH));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/Bits.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/Bits.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/Bits.java
new file mode 100644
index 0000000..92cef47
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/Bits.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 Clearspring Technologies, Inc.
+ *
+ * Licensed 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.geode.redis.internal.hll;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+
+public class Bits {
+
+  public static int[] getBits(byte[] mBytes) throws IOException {
+    int bitSize = mBytes.length / 4;
+    int[] bits = new int[bitSize];
+    DataInputStream dis = new DataInputStream(new ByteArrayInputStream(mBytes));
+    for (int i = 0; i < bitSize; i++) {
+      bits[i] = dis.readInt();
+    }
+    return bits;
+  }
+
+  /**
+   * This method might be better described as "byte array to int array" or "data input to int array"
+   */
+  public static int[] getBits(DataInput dataIn, int byteLength) throws IOException {
+    int bitSize = byteLength / 4;
+    int[] bits = new int[bitSize];
+    for (int i = 0; i < bitSize; i++) {
+      bits[i] = dataIn.readInt();
+    }
+    return bits;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/CardinalityMergeException.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/CardinalityMergeException.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/CardinalityMergeException.java
new file mode 100644
index 0000000..561c3ef
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/CardinalityMergeException.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011 Clearspring Technologies, Inc.
+ *
+ * Licensed 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.geode.redis.internal.hll;
+
+@SuppressWarnings("serial")
+public abstract class CardinalityMergeException extends Exception {
+
+  public CardinalityMergeException(String message) {
+    super(message);
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/c6dbc6d4/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/HyperLogLog.java
----------------------------------------------------------------------
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/HyperLogLog.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/HyperLogLog.java
new file mode 100644
index 0000000..b89512e
--- /dev/null
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/hll/HyperLogLog.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2012 Clearspring Technologies, Inc.
+ *
+ * Licensed 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.geode.redis.internal.hll;
+
+import org.apache.geode.redis.internal.executor.hll.HllExecutor;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+
+/**
+ * Java implementation of HyperLogLog (HLL) algorithm from this paper:
+ * <p/>
+ * http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf
+ * <p/>
+ * HLL is an improved version of LogLog that is capable of estimating the cardinality of a set with
+ * accuracy = 1.04/sqrt(m) where m = 2^b. So we can control accuracy vs space usage by increasing or
+ * decreasing b.
+ * <p/>
+ * The main benefit of using HLL over LL is that it only requires 64% of the space that LL does to
+ * get the same accuracy.
+ * <p/>
+ * This implementation implements a single counter. If a large (millions) number of counters are
+ * required you may want to refer to:
+ * <p/>
+ * http://dsiutils.dsi.unimi.it/
+ * <p/>
+ * It has a more complex implementation of HLL that supports multiple counters in a single object,
+ * drastically reducing the java overhead from creating a large number of objects.
+ * <p/>
+ * This implementation leveraged a javascript implementation that Yammer has been working on:
+ * <p/>
+ * https://github.com/yammer/probablyjs
+ * <p>
+ * Note that this implementation does not include the long range correction function defined in the
+ * original paper. Empirical evidence shows that the correction function causes more harm than good.
+ * </p>
+ * <p/>
+ * <p>
+ * Users have different motivations to use different types of hashing functions. Rather than try to
+ * keep up with all available hash functions and to remove the concern of causing future binary
+ * incompatibilities this class allows clients to offer the value in hashed int or long form. This
+ * way clients are free to change their hash function on their own time line. We recommend using
+ * Google's Guava Murmur3_128 implementation as it provides good performance and speed when high
+ * precision is required. In our tests the 32bit MurmurHash function included in this project is
+ * faster and produces better results than the 32 bit murmur3 implementation google provides.
+ * </p>
+ */
+public class HyperLogLog implements ICardinality, Serializable {
+
+  private static final long serialVersionUID = -4661220245111112301L;
+  private final RegisterSet registerSet;
+  private final int log2m;
+  private final double alphaMM;
+
+
+  /**
+   * Create a new HyperLogLog instance using the specified standard deviation.
+   *
+   * @param rsd - the relative standard deviation for the counter. smaller values create counters
+   *        that require more space.
+   */
+  public HyperLogLog(double rsd) {
+    this(log2m(rsd));
+  }
+
+  private static int log2m(double rsd) {
+    return (int) (Math.log((1.106 / rsd) * (1.106 / rsd)) / Math.log(2));
+  }
+
+  /**
+   * Create a new HyperLogLog instance. The log2m parameter defines the accuracy of the counter. The
+   * larger the log2m the better the accuracy.
+   * <p/>
+   * accuracy = 1.04/sqrt(2^log2m)
+   *
+   * @param log2m - the number of bits to use as the basis for the HLL instance
+   */
+  public HyperLogLog(int log2m) {
+    this(log2m, new RegisterSet(1 << log2m));
+  }
+
+  /**
+   * Creates a new HyperLogLog instance using the given registers. Used for unmarshalling a
+   * serialized instance and for merging multiple counters together.
+   *
+   * @param registerSet - the initial values for the register set
+   */
+  @Deprecated
+  public HyperLogLog(int log2m, RegisterSet registerSet) {
+    if (log2m < 0 || log2m > 30) {
+      throw new IllegalArgumentException(
+          "log2m argument is " + log2m + " and is outside the range [0, 30]");
+    }
+    this.registerSet = registerSet;
+    this.log2m = log2m;
+    int m = 1 << this.log2m;
+
+    alphaMM = getAlphaMM(log2m, m);
+  }
+
+  @Override
+  public boolean offerHashed(long hashedValue) {
+    // j becomes the binary address determined by the first b log2m of x
+    // j will be between 0 and 2^log2m
+    final int j = (int) (hashedValue >>> (Long.SIZE - log2m));
+    final int r =
+        Long.numberOfLeadingZeros((hashedValue << this.log2m) | (1 << (this.log2m - 1)) + 1) + 1;
+    return registerSet.updateIfGreater(j, r);
+  }
+
+  @Override
+  public boolean offerHashed(int hashedValue) {
+    // j becomes the binary address determined by the first b log2m of x
+    // j will be between 0 and 2^log2m
+    final int j = hashedValue >>> (Integer.SIZE - log2m);
+    final int r =
+        Integer.numberOfLeadingZeros((hashedValue << this.log2m) | (1 << (this.log2m - 1)) + 1) + 1;
+    return registerSet.updateIfGreater(j, r);
+  }
+
+  @Override
+  public boolean offer(Object o) {
+    final int x = MurmurHash.hash(o);
+    return offerHashed(x);
+  }
+
+
+  @Override
+  public long cardinality() {
+    double registerSum = 0;
+    int count = registerSet.count;
+    double zeros = 0.0;
+    for (int j = 0; j < registerSet.count; j++) {
+      int val = registerSet.get(j);
+      registerSum += 1.0 / (1 << val);
+      if (val == 0) {
+        zeros++;
+      }
+    }
+
+    double estimate = alphaMM * (1 / registerSum);
+
+    if (estimate <= (5.0 / 2.0) * count) {
+      // Small Range Estimate
+      return Math.round(linearCounting(count, zeros));
+    } else {
+      return Math.round(estimate);
+    }
+  }
+
+  @Override
+  public int sizeof() {
+    return registerSet.size * 4;
+  }
+
+  @Override
+  public byte[] getBytes() throws IOException {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    DataOutput dos = new DataOutputStream(baos);
+    writeBytes(dos);
+
+    return baos.toByteArray();
+  }
+
+  private void writeBytes(DataOutput serializedByteStream) throws IOException {
+    serializedByteStream.writeInt(log2m);
+    serializedByteStream.writeInt(registerSet.size * 4);
+    for (int x : registerSet.readOnlyBits()) {
+      serializedByteStream.writeInt(x);
+    }
+  }
+
+  /**
+   * Add all the elements of the other set to this set.
+   * <p/>
+   * This operation does not imply a loss of precision.
+   *
+   * @param other A compatible Hyperloglog instance (same log2m)
+   * @throws CardinalityMergeException if other is not compatible
+   */
+  public void addAll(HyperLogLog other) throws CardinalityMergeException {
+    if (this.sizeof() != other.sizeof()) {
+      throw new HyperLogLogMergeException("Cannot merge estimators of different sizes");
+    }
+
+    registerSet.merge(other.registerSet);
+  }
+
+  @Override
+  public ICardinality merge(ICardinality... estimators) throws CardinalityMergeException {
+    HyperLogLog merged = new HyperLogLog(HllExecutor.DEFAULT_HLL_STD_DEV);// new HyperLogLog(log2m,
+                                                                          // new
+                                                                          // RegisterSet(this.registerSet.count));
+    merged.addAll(this);
+
+    if (estimators == null) {
+      return merged;
+    }
+
+    for (ICardinality estimator : estimators) {
+      if (!(estimator instanceof HyperLogLog)) {
+        throw new HyperLogLogMergeException("Cannot merge estimators of different class");
+      }
+      HyperLogLog hll = (HyperLogLog) estimator;
+      merged.addAll(hll);
+    }
+
+    return merged;
+  }
+
+  private Object writeReplace() {
+    return new SerializationHolder(this);
+  }
+
+  /**
+   * This class exists to support Externalizable semantics for HyperLogLog objects without having to
+   * expose a public constructor, public write/read methods, or pretend final fields aren't final.
+   *
+   * In short, Externalizable allows you to skip some of the more verbose meta-data default
+   * Serializable gets you, but still includes the class name. In that sense, there is some cost to
+   * this holder object because it has a longer class name. I imagine people who care about
+   * optimizing for that have their own work-around for long class names in general, or just use a
+   * custom serialization framework. Therefore we make no attempt to optimize that here (eg. by
+   * raising this from an inner class and giving it an unhelpful name).
+   */
+  private static class SerializationHolder implements Externalizable {
+
+    HyperLogLog hyperLogLogHolder;
+
+    public SerializationHolder(HyperLogLog hyperLogLogHolder) {
+      this.hyperLogLogHolder = hyperLogLogHolder;
+    }
+
+    /**
+     * required for Externalizable
+     */
+    @SuppressWarnings("unused")
+    public SerializationHolder() {
+
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+      hyperLogLogHolder.writeBytes(out);
+    }
+
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+      hyperLogLogHolder = Builder.build(in);
+    }
+
+    private Object readResolve() {
+      return hyperLogLogHolder;
+    }
+  }
+
+  public static class Builder implements IBuilder<ICardinality>, Serializable {
+
+    private static final long serialVersionUID = -979314356097156719L;
+    private double rsd;
+
+    public Builder(double rsd) {
+      this.rsd = rsd;
+    }
+
+    @Override
+    public HyperLogLog build() {
+      return new HyperLogLog(rsd);
+    }
+
+    @Override
+    public int sizeof() {
+      int log2m = log2m(rsd);
+      int k = 1 << log2m;
+      return RegisterSet.getBits(k) * 4;
+    }
+
+    public static HyperLogLog build(byte[] bytes) throws IOException {
+      ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+      return build(new DataInputStream(bais));
+    }
+
+    public static HyperLogLog build(DataInput serializedByteStream) throws IOException {
+      int log2m = serializedByteStream.readInt();
+      int byteArraySize = serializedByteStream.readInt();
+      return new HyperLogLog(log2m,
+          new RegisterSet(1 << log2m, Bits.getBits(serializedByteStream, byteArraySize)));
+    }
+  }
+
+  @SuppressWarnings("serial")
+  protected static class HyperLogLogMergeException extends CardinalityMergeException {
+
+    public HyperLogLogMergeException(String message) {
+      super(message);
+    }
+  }
+
+  protected static double getAlphaMM(final int p, final int m) {
+    // See the paper.
+    switch (p) {
+      case 4:
+        return 0.673 * m * m;
+      case 5:
+        return 0.697 * m * m;
+      case 6:
+        return 0.709 * m * m;
+      default:
+        return (0.7213 / (1 + 1.079 / m)) * m * m;
+    }
+  }
+
+  protected static double linearCounting(int m, double V) {
+    return m * Math.log(m / V);
+  }
+}


Mime
View raw message