/*
* Copyright 2014-2015 the original author or authors.
*
* 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.springframework.integration.redis.outbound;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.expression.ExpressionUtils;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.redis.support.RedisHeaders;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* The Gateway component implementation to perform Redis commands with provided arguments and to return command result.
*
* @author Artem Bilan
* @since 4.0
*/
public class RedisOutboundGateway extends AbstractReplyProducingMessageHandler {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private final RedisTemplate<?, ?> redisTemplate;
private EvaluationContext evaluationContext;
private volatile RedisSerializer<Object> argumentsSerializer = new GenericToStringSerializer<Object>(Object.class);
private volatile Expression commandExpression = PARSER.parseExpression("headers[" + RedisHeaders.COMMAND + "]");
private volatile ArgumentsStrategy argumentsStrategy = new PayloadArgumentsStrategy();
public RedisOutboundGateway(RedisTemplate<?, ?> redisTemplate) {
Assert.notNull(redisTemplate, "'redisTemplate' must not be null");
this.redisTemplate = redisTemplate;
}
public RedisOutboundGateway(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "'connectionFactory' must not be null");
this.redisTemplate = new RedisTemplate<Object, Object>();
this.redisTemplate.setConnectionFactory(connectionFactory);
this.redisTemplate.afterPropertiesSet();
}
@SuppressWarnings("unchecked")
public void setArgumentsSerializer(RedisSerializer<?> serializer) {
Assert.notNull(serializer, "'serializer' must not be null");
this.argumentsSerializer = (RedisSerializer<Object>) serializer;
}
/**
* @param commandExpression the String in SpEL syntax.
* @since 4.3
*/
public void setCommandExpression(Expression commandExpression) {
this.commandExpression = commandExpression;
}
/**
* @param commandExpression the String in SpEL syntax.
* @since 4.3
*/
public void setCommandExpressionString(String commandExpression) {
Assert.hasText(commandExpression, "'commandExpression' must not be empty");
this.commandExpression = EXPRESSION_PARSER.parseExpression(commandExpression);
}
public void setArgumentsStrategy(ArgumentsStrategy argumentsStrategy) {
this.argumentsStrategy = argumentsStrategy;
}
public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) {
this.evaluationContext = evaluationContext;
}
@Override
public String getComponentType() {
return "redis:outbound-gateway";
}
@Override
protected void doInit() {
super.doInit();
if (this.evaluationContext == null) {
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
}
}
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
final String command = this.commandExpression.getValue(this.evaluationContext, requestMessage, String.class);
Assert.notNull(command, "The 'command' must not evaluate to 'null'.");
byte[][] args = null;
if (this.argumentsStrategy != null) {
Object[] arguments = this.argumentsStrategy.resolve(command, requestMessage);
if (!ObjectUtils.isEmpty(arguments)) {
args = new byte[arguments.length][];
for (int i = 0; i < arguments.length; i++) {
Object argument = arguments[i];
byte[] arg = null;
if (argument instanceof byte[]) {
arg = (byte[]) argument;
}
else {
arg = this.argumentsSerializer.serialize(argument);
}
args[i] = arg;
}
}
}
final byte[][] actualArgs = args;
return this.redisTemplate.execute(
(RedisCallback<Object>) connection -> connection.execute(command, actualArgs));
}
private static class PayloadArgumentsStrategy implements ArgumentsStrategy {
PayloadArgumentsStrategy() {
super();
}
@Override
public Object[] resolve(String command, Message<?> message) {
Object payload = message.getPayload();
if (payload instanceof Object[]) {
return (Object[]) payload;
}
else {
return new Object[]{payload};
}
}
}
}