/* * Copyright 2002-2016 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.inbound; import java.util.Collection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.support.collections.RedisCollectionFactoryBean; import org.springframework.data.redis.support.collections.RedisCollectionFactoryBean.CollectionType; import org.springframework.data.redis.support.collections.RedisStore; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.core.MessageSource; import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.transaction.IntegrationResourceHolder; import org.springframework.messaging.Message; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; /** * Inbound channel adapter which returns a Message representing a view into * a Redis store. The type of store depends on the {@link #collectionType} attribute. * Default is LIST. This adapter supports 5 types of collections identified by * {@link CollectionType} * * @author Oleg Zhurakousky * @author Gary Russell * @since 2.2 */ public class RedisStoreMessageSource extends IntegrationObjectSupport implements MessageSource<RedisStore> { private final ThreadLocal<RedisStore> resourceHolder = new ThreadLocal<RedisStore>(); private volatile StandardEvaluationContext evaluationContext; private volatile Expression keyExpression; private volatile CollectionType collectionType = CollectionType.LIST; private final RedisTemplate<String, ?> redisTemplate; /** * Creates an instance with the provided {@link RedisTemplate} and SpEL expression * which should resolve to a 'key' name of the collection to be used. * It assumes that {@link RedisTemplate} is fully initialized and ready to be used. * The 'keyExpression' will be evaluated on every call to the {@link #receive()} method. * * @param redisTemplate The Redis template. * @param keyExpression The key expression. */ public RedisStoreMessageSource(RedisTemplate<String, ?> redisTemplate, Expression keyExpression) { Assert.notNull(keyExpression, "'keyExpression' must not be null"); Assert.notNull(redisTemplate, "'redisTemplate' must not be null"); this.redisTemplate = redisTemplate; this.keyExpression = keyExpression; } /** * Creates an instance with the provided {@link RedisConnectionFactory} and SpEL expression * which should resolve to a 'key' name of the collection to be used. * It will create and initialize an instance of {@link StringRedisTemplate} that uses * {@link StringRedisSerializer} for all serialization. * * The 'keyExpression' will be evaluated on every call to the {@link #receive()} method. * * @param connectionFactory The connection factory. * @param keyExpression The key expression. */ public RedisStoreMessageSource(RedisConnectionFactory connectionFactory, Expression keyExpression) { Assert.notNull(keyExpression, "'keyExpression' must not be null"); Assert.notNull(connectionFactory, "'connectionFactory' must not be null"); StringRedisTemplate redisTemplate = new StringRedisTemplate(); redisTemplate.setConnectionFactory(connectionFactory); redisTemplate.afterPropertiesSet(); this.redisTemplate = redisTemplate; this.keyExpression = keyExpression; } public void setCollectionType(CollectionType collectionType) { this.collectionType = collectionType; } /** * Returns a Message with the view into a {@link RedisStore} identified * by {@link #keyExpression} */ @Override @SuppressWarnings("unchecked") public Message<RedisStore> receive() { String key = this.keyExpression.getValue(this.evaluationContext, String.class); Assert.hasText(key, "Failed to determine the key for the collection"); RedisStore store = this.createStoreView(key); Object holder = TransactionSynchronizationManager.getResource(this); if (holder != null) { Assert.isInstanceOf(IntegrationResourceHolder.class, holder); ((IntegrationResourceHolder) holder).addAttribute("store", store); } if (store instanceof Collection<?> && ((Collection<Object>) store).size() < 1) { return null; } else { return this.getMessageBuilderFactory().withPayload(store).build(); } } private RedisStore createStoreView(String key) { RedisCollectionFactoryBean fb = new RedisCollectionFactoryBean(); fb.setKey(key); fb.setTemplate(this.redisTemplate); fb.setType(this.collectionType); fb.afterPropertiesSet(); return fb.getObject(); } @Override public String getComponentType() { return "redis:store-inbound-channel-adapter"; } @Override protected void onInit() throws Exception { this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); } public RedisStore getResource() { return this.resourceHolder.get(); } public void afterCommit(Object object) { this.resourceHolder.remove(); } public void afterRollback(Object object) { this.resourceHolder.remove(); } }