/*
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.collections.RedisZSet;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.redis.rules.RedisAvailable;
import org.springframework.integration.redis.rules.RedisAvailableTests;
import org.springframework.messaging.Message;
import org.springframework.messaging.SubscribableChannel;
/**
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
* @since 2.2
*/
public class RedisStoreInboundChannelAdapterIntegrationTests extends RedisAvailableTests {
@Test
@RedisAvailable
@SuppressWarnings("unchecked")
public void testListInboundConfiguration() throws Exception {
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
this.prepareList(jcf);
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("list-inbound-adapter.xml", this.getClass());
SourcePollingChannelAdapter spca = context.getBean("listAdapter", SourcePollingChannelAdapter.class);
spca.start();
QueueChannel redisChannel = context.getBean("redisChannel", QueueChannel.class);
Message<Integer> message = (Message<Integer>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(Integer.valueOf(13), message.getPayload());
//poll again, should get the same stuff
message = (Message<Integer>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(Integer.valueOf(13), message.getPayload());
this.deletePresidents(jcf);
context.close();
}
@Test
@RedisAvailable
@SuppressWarnings("unchecked")
// synchronization commit renames the list
public void testListInboundConfigurationWithSynchronization() throws Exception {
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
StringRedisTemplate template = this.createStringRedisTemplate(jcf);
template.delete("bar");
this.prepareList(jcf);
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("list-inbound-adapter.xml", this.getClass());
SourcePollingChannelAdapter spca =
context.getBean("listAdapterWithSynchronization", SourcePollingChannelAdapter.class);
spca.start();
QueueChannel redisChannel = context.getBean("redisChannel", QueueChannel.class);
Message<Integer> message = (Message<Integer>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(Integer.valueOf(13), message.getPayload());
//poll again, should get nothing since the collection was removed during synchronization
message = (Message<Integer>) redisChannel.receive(100);
assertNull(message);
int n = 0;
while (n++ < 100 && template.keys("bar").size() == 0) {
Thread.sleep(100);
}
assertTrue("Rename didn't occur", n < 100);
assertEquals(Long.valueOf(13), template.boundListOps("bar").size());
template.delete("bar");
spca.stop();
context.close();
}
@SuppressWarnings("resource")
@Test
@RedisAvailable
// synchronization rollback renames the list
public void testListInboundConfigurationWithSynchronizationAndRollback() throws Exception {
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
StringRedisTemplate template = this.createStringRedisTemplate(jcf);
template.delete("baz");
this.prepareList(jcf);
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("list-inbound-adapter.xml",
this.getClass());
SubscribableChannel fail = context.getBean("redisFailChannel", SubscribableChannel.class);
final CountDownLatch latch = new CountDownLatch(1);
fail.subscribe(message -> {
latch.countDown();
throw new RuntimeException("Test Rollback");
});
SourcePollingChannelAdapter spca = context.getBean("listAdapterWithSynchronizationAndRollback",
SourcePollingChannelAdapter.class);
spca.start();
assertTrue(latch.await(10, TimeUnit.SECONDS));
int n = 0;
while (n++ < 100 && template.keys("baz").size() == 0) {
Thread.sleep(100);
}
assertTrue("Rename didn't occur", n < 100);
assertEquals(Long.valueOf(13), template.boundListOps("baz").size());
template.delete("baz");
spca.stop();
context.close();
}
@Test
@RedisAvailable
@SuppressWarnings("unchecked")
// synchronization commit renames the list
public void testListInboundConfigurationWithSynchronizationAndTemplate() throws Exception {
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
StringRedisTemplate template = this.createStringRedisTemplate(jcf);
template.delete("bar");
this.prepareList(jcf);
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("list-inbound-adapter.xml", this.getClass());
SourcePollingChannelAdapter spca =
context.getBean("listAdapterWithSynchronizationAndRedisTemplate", SourcePollingChannelAdapter.class);
spca.start();
QueueChannel redisChannel = context.getBean("redisChannel", QueueChannel.class);
Message<Integer> message = (Message<Integer>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(Integer.valueOf(13), message.getPayload());
//poll again, should get nothing since the collection was removed during synchronization
message = (Message<Integer>) redisChannel.receive(100);
assertNull(message);
int n = 0;
while (n++ < 100 && template.keys("bar").size() == 0) {
Thread.sleep(100);
}
assertTrue("Rename didn't occur", n < 100);
assertEquals(Long.valueOf(13), template.boundListOps("bar").size());
template.delete("bar");
spca.stop();
context.close();
}
@Test
@RedisAvailable
@SuppressWarnings("unchecked")
public void testZsetInboundAdapter() throws InterruptedException {
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
this.prepareZset(jcf);
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("zset-inbound-adapter.xml", this.getClass());
//No Score test
SourcePollingChannelAdapter zsetAdapterNoScore =
context.getBean("zsetAdapterNoScore", SourcePollingChannelAdapter.class);
zsetAdapterNoScore.start();
QueueChannel redisChannel = context.getBean("redisChannel", QueueChannel.class);
Message<RedisZSet<Object>> message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(13, message.getPayload().size());
//poll again, should get the same stuff
message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(13, message.getPayload().size());
zsetAdapterNoScore.stop();
//ScoreRange test
SourcePollingChannelAdapter zsetAdapterWithScoreRange =
context.getBean("zsetAdapterWithScoreRange", SourcePollingChannelAdapter.class);
zsetAdapterWithScoreRange.start();
message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(11, message.getPayload().rangeByScore(18, 20).size());
//poll again, should get the same stuff
message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(11, message.getPayload().rangeByScore(18, 20).size());
zsetAdapterWithScoreRange.stop();
//SingleScore test
SourcePollingChannelAdapter zsetAdapterWithSingleScore =
context.getBean("zsetAdapterWithSingleScore", SourcePollingChannelAdapter.class);
zsetAdapterWithSingleScore.start();
message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(2, message.getPayload().rangeByScore(18, 18).size());
//poll again, should get the same stuff
message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(2, message.getPayload().rangeByScore(18, 18).size());
zsetAdapterWithSingleScore.stop();
//SingleScoreAndSynchronization test
SourcePollingChannelAdapter zsetAdapterWithSingleScoreAndSynchronization =
context.getBean("zsetAdapterWithSingleScoreAndSynchronization", SourcePollingChannelAdapter.class);
QueueChannel otherRedisChannel = context.getBean("otherRedisChannel", QueueChannel.class);
// get all 13 presidents
zsetAdapterNoScore.start();
message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
assertEquals(13, message.getPayload().size());
zsetAdapterNoScore.stop();
// get only presidents for 18th century
zsetAdapterWithSingleScoreAndSynchronization.start();
Message<Integer> sizeMessage = (Message<Integer>) otherRedisChannel.receive(10000);
assertNotNull(sizeMessage);
assertEquals(Integer.valueOf(2), sizeMessage.getPayload());
// ... however other elements are still available 13-2=11
zsetAdapterNoScore.start();
message = (Message<RedisZSet<Object>>) redisChannel.receive(10000);
assertNotNull(message);
int n = 0;
while (n++ < 100 && message.getPayload().size() != 11) {
Thread.sleep(100);
}
assertTrue(n < 100);
zsetAdapterNoScore.stop();
zsetAdapterWithSingleScoreAndSynchronization.stop();
this.deletePresidents(jcf);
context.close();
}
}