blob: 6aa154b33f7e1b4c496bc73a657d05b23eb0e389 [file] [log] [blame]
/*
* 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.netty;
import java.nio.channels.SocketChannel;
import java.util.List;
import java.util.stream.Collectors;
import io.netty.channel.ChannelHandlerContext;
import org.apache.geode.redis.internal.RedisCommandType;
import org.apache.geode.redis.internal.data.ByteArrayWrapper;
import org.apache.geode.redis.internal.executor.RedisResponse;
/**
* The command class is used in holding a received Redis command. Each sent command resides in an
* instance of this class. This class is designed to be used strictly by getter and setter methods.
*/
public class Command {
private final List<byte[]> commandElems;
private final RedisCommandType commandType;
private String key;
private ByteArrayWrapper bytes;
/**
* Constructor used to create a static marker Command for shutting down executors.
*/
Command() {
commandElems = null;
commandType = null;
}
/**
* Constructor for {@link Command}. Must initialize Command with a {@link SocketChannel} and a
* {@link List} of command elements
*
* @param commandElems List of elements in command
*/
public Command(List<byte[]> commandElems) {
if (commandElems == null || commandElems.isEmpty()) {
throw new IllegalArgumentException(
"List of command elements cannot be empty -> List:" + commandElems);
}
this.commandElems = commandElems;
RedisCommandType type;
try {
byte[] charCommand = commandElems.get(0);
String commandName = Coder.bytesToString(charCommand).toUpperCase();
type = RedisCommandType.valueOf(commandName);
} catch (Exception e) {
type = RedisCommandType.UNKNOWN;
}
this.commandType = type;
}
public boolean isSupported() {
return commandType.isSupported();
}
public boolean isUnsupported() {
return commandType.isUnsupported();
}
public boolean isUnimplemented() {
return commandType.isUnimplemented();
}
public boolean isUnknown() {
return commandType.isUnknown();
}
/**
* Used to get the command element list
*
* @return List of command elements in form of {@link List}
*/
public List<byte[]> getProcessedCommand() {
return this.commandElems;
}
/**
* Used to get the command element list
*
* @return List of command elements in form of {@link List}
*/
public List<ByteArrayWrapper> getProcessedCommandWrappers() {
return this.commandElems.stream().map(ByteArrayWrapper::new).collect(Collectors.toList());
}
/**
* Getter method for the command type
*
* @return The command type
*/
public RedisCommandType getCommandType() {
return this.commandType;
}
/**
* Convenience method to get a String representation of the key in a Redis command, always at the
* second position in the sent command array
*
* @return Returns the second element in the parsed command list, which is always the key for
* commands indicating a key
*/
public String getStringKey() {
if (this.commandElems.size() > 1) {
if (this.bytes == null) {
this.bytes = new ByteArrayWrapper(this.commandElems.get(1));
this.key = this.bytes.toString();
} else if (this.key == null) {
this.key = this.bytes.toString();
}
return this.key;
} else {
return null;
}
}
public ByteArrayWrapper getKey() {
if (this.commandElems.size() > 1) {
if (this.bytes == null) {
this.bytes = new ByteArrayWrapper(this.commandElems.get(1));
}
return this.bytes;
} else {
return null;
}
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
for (byte[] rawCommand : this.commandElems) {
b.append(getHexEncodedString(rawCommand));
b.append(' ');
}
return b.toString();
}
public static String getHexEncodedString(byte[] data) {
return getHexEncodedString(data, data.length);
}
public static String getHexEncodedString(byte[] data, int size) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++) {
byte aByte = data[i];
if (aByte > 31 && aByte < 127) {
builder.append((char) aByte);
} else {
if (aByte == 0x0a) {
builder.append("\\n");
} else if (aByte == 0x0d) {
builder.append("\\r");
} else {
builder.append(String.format("\\x%02x", aByte));
}
}
}
return builder.toString();
}
public RedisResponse execute(ExecutionHandlerContext executionHandlerContext) {
RedisCommandType type = getCommandType();
return type.executeCommand(this, executionHandlerContext);
}
public boolean isOfType(RedisCommandType type) {
return type == getCommandType();
}
public String wrongNumberOfArgumentsErrorMessage() {
String result;
result = String.format("wrong number of arguments for '%s' command",
getCommandType().toString().toLowerCase());
return result;
}
private long asyncStartTime;
public void setAsyncStartTime(long start) {
asyncStartTime = start;
}
public long getAsyncStartTime() {
return asyncStartTime;
}
private ChannelHandlerContext channelHandlerContext;
public void setChannelHandlerContext(ChannelHandlerContext ctx) {
channelHandlerContext = ctx;
}
public ChannelHandlerContext getChannelHandlerContext() {
return channelHandlerContext;
}
}