/**
* Copyright 2024 Beijing Baolande Software Corporation
*
* 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.
*
* Runtime Library Exception to the Apache 2.0 License:
*
* As an exception, if you use this Software to compile your source code and
* portions of this Software are embedded into the binary product as a result,
* you may redistribute such product without providing attribution as would
* otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
*/
package redis_sdk.client.api
public open class RedisCommand {
private let commandType: CommandType
private let commandTypeArg: CommandArg
private let commandArgs: CommandArgs
private var supportResponse: Bool
private var response: ?RedisMessage = None<RedisMessage>
private var exception: ?Exception = None<Exception>
private let mutex = Mutex()
private let monitor = synchronized (mutex) {
mutex.condition()
}
public init(commandType: CommandType) {
this.commandType = commandType
this.commandTypeArg = BytesArg(commandType.getBytes())
this.commandArgs = CommandArgs()
this.supportResponse = true
}
public init(commandType: CommandType, supportResponse!: Bool) {
this.commandType = commandType
this.commandTypeArg = BytesArg(commandType.getBytes())
this.commandArgs = CommandArgs()
this.supportResponse = supportResponse
}
public init(commandType: CommandType, commandArgs: CommandArgs) {
this.commandType = commandType
this.commandTypeArg = BytesArg(commandType.getBytes())
this.commandArgs = commandArgs
this.supportResponse = true
}
public init(commandType: CommandType, commandArgs: CommandArgs, supportResponse!: Bool) {
this.commandType = commandType
this.commandTypeArg = BytesArg(commandType.getBytes())
this.commandArgs = commandArgs
this.supportResponse = supportResponse
}
public init(commandType: CommandType, commandArgs: CommandArgs, compositeArg: CompositeArg) {
this.commandType = commandType
this.commandTypeArg = BytesArg(commandType.getBytes())
this.commandArgs = commandArgs
compositeArg.buildArgs(this.commandArgs)
this.supportResponse = true
}
public init(commandType: CommandType, commandArgs: CommandArgs, compositeArg: CompositeArg, supportResponse!: Bool) {
this.commandType = commandType
this.commandTypeArg = BytesArg(commandType.getBytes())
this.commandArgs = commandArgs
compositeArg.buildArgs(this.commandArgs)
this.supportResponse = supportResponse
}
public func getCommandType(): CommandType {
return commandType
}
public func getCommandArgs(): CommandArgs {
return commandArgs
}
public func getEncodeSize() {
let count = commandArgs.getArgsSize() + 1
let countBytes = count.toString().toArray()
return 1 + countBytes.size + RedisConstants.CRLF.size + // 参数个数
commandTypeArg.getEncodeSize() + // 命令名称
commandArgs.getEncodeSize() // 命令参数
}
public open func encode(buffer: ByteBuffer): Unit {
buffer.put(RedisConstants.ASTERISK_BYTE)
let count = commandArgs.getArgsSize() + 1
writeInteger(count, buffer)
buffer.put(RedisConstants.CRLF)
commandTypeArg.encode(buffer)
commandArgs.encode(buffer)
}
public func setResponse(response: RedisMessage) {
this.response = response
synchronized(mutex) {
monitor.notifyAll()
}
}
public func setException(exception: Exception) {
this.exception = exception
synchronized(mutex) {
monitor.notifyAll()
}
}
public func getResponse(): ?RedisMessage {
if (!supportResponse) {
throw RedisException("The command ${commandType.name()} does not have a response")
}
if (let Some(exception) <- exception) {
if (let Some(redisException) <- (exception as RedisException)) {
throw redisException
} else {
throw RedisException(exception.message, exception)
}
}
return this.response
}
public func waitForResponse(duration: Duration): RedisMessage {
if (!supportResponse) {
throw RedisException("The command ${commandType.name()} does not have a response")
}
let endTime = DateTime.now().toUnixTimeStamp().toMilliseconds() + duration.toMilliseconds()
var remainingTime = duration.toMilliseconds()
synchronized(mutex) {
while (true) {
if (let Some(message) <- response) {
return message
}
if (let Some(exception) <- exception) {
if (let Some(redisException) <- (exception as RedisException)) {
throw redisException
} else {
throw RedisException(exception.message, exception)
}
}
if (remainingTime <= 0) {
break
}
monitor.wait(timeout: Duration.millisecond * remainingTime)
remainingTime = endTime - DateTime.now().toUnixTimeStamp().toMilliseconds()
}
}
throw RedisTimeoutException("Unable to obtain response in ${duration}")
}
private static func writeInteger(value: Int64, buffer: ByteBuffer) {
buffer.put(value.toString().toArray())
}
public func clear() {
synchronized(mutex) {
response = None
exception = None
}
}
}
public open class ParameterizedRedisCommand<T> <: RedisCommand {
private let builder: ResponseBuilder<T>
public init(commandType: CommandType, builder: ResponseBuilder<T>) {
super(commandType)
this.builder = builder
}
public init(commandType: CommandType, commandArgs: CommandArgs, builder: ResponseBuilder<T>) {
super(commandType, commandArgs)
this.builder = builder
}
public init(
commandType: CommandType,
commandArgs: CommandArgs,
compositeArg: CompositeArg,
builder: ResponseBuilder<T>
) {
super(commandType, commandArgs, compositeArg)
this.builder = builder
}
public func getBuilder() {
return builder
}
public func buildResponse(waitResponseTimeout: Duration): T {
let redisMessage = this.waitForResponse(waitResponseTimeout)
return builder.build(redisMessage)
}
}