/*
* Copyright 2011-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.data.redis.listener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.data.redis.ConnectionFactoryTracker;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceTestClientResources;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
/**
* Integration tests confirming that {@link RedisMessageListenerContainer} closes connections after unsubscribing
*
* @author Jennifer Hickey
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(Parameterized.class)
public class SubscriptionConnectionTests {
private static final Log logger = LogFactory.getLog(SubscriptionConnectionTests.class);
private static final String CHANNEL = "pubsub::test";
private RedisConnectionFactory connectionFactory;
private List<RedisMessageListenerContainer> containers = new ArrayList<RedisMessageListenerContainer>();
private final Object handler = new Object() {
@SuppressWarnings("unused")
public void handleMessage(String message) {
logger.debug(message);
}
};
public SubscriptionConnectionTests(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
ConnectionFactoryTracker.add(connectionFactory);
}
@After
public void tearDown() throws Exception {
for (RedisMessageListenerContainer container : containers) {
if (container.isActive()) {
container.destroy();
}
}
}
@AfterClass
public static void cleanUp() {
ConnectionFactoryTracker.cleanUp();
}
@Parameters
public static Collection<Object[]> testParams() {
int port = SettingsUtils.getPort();
String host = SettingsUtils.getHost();
// Jedis
JedisConnectionFactory jedisConnFactory = new JedisConnectionFactory();
jedisConnFactory.setPort(port);
jedisConnFactory.setHostName(host);
jedisConnFactory.setDatabase(2);
jedisConnFactory.afterPropertiesSet();
// Lettuce
LettuceConnectionFactory lettuceConnFactory = new LettuceConnectionFactory();
lettuceConnFactory.setClientResources(LettuceTestClientResources.getSharedClientResources());
lettuceConnFactory.setPort(port);
lettuceConnFactory.setHostName(host);
lettuceConnFactory.setDatabase(2);
lettuceConnFactory.setValidateConnection(true);
lettuceConnFactory.afterPropertiesSet();
return Arrays.asList(new Object[][] { { jedisConnFactory }, { lettuceConnFactory } });
}
@Test
public void testStopMessageListenerContainers() throws Exception {
// Grab all 8 connections from the pool. They should be released on
// container stop
for (int i = 0; i < 8; i++) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setBeanName("container" + i);
container.addMessageListener(new MessageListenerAdapter(handler), Arrays.asList(new ChannelTopic(CHANNEL)));
container.setTaskExecutor(new SyncTaskExecutor());
container.setSubscriptionExecutor(new SimpleAsyncTaskExecutor());
container.afterPropertiesSet();
container.start();
if (connectionFactory instanceof JedisConnectionFactory) {
// Need to sleep shortly as jedis cannot deal propery with multiple repsonses within one connection
// see https://github.com/xetorthio/jedis/issues/186
Thread.sleep(100);
}
container.stop();
containers.add(container);
}
// verify we can now get a connection from the pool
RedisConnection connection = connectionFactory.getConnection();
connection.close();
}
@Test
public void testRemoveLastListener() throws Exception {
// Grab all 8 connections from the pool
MessageListener listener = new MessageListenerAdapter(handler);
for (int i = 0; i < 8; i++) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setBeanName("container" + i);
container.addMessageListener(listener, Arrays.asList(new ChannelTopic(CHANNEL)));
container.setTaskExecutor(new SyncTaskExecutor());
container.setSubscriptionExecutor(new SimpleAsyncTaskExecutor());
container.afterPropertiesSet();
container.start();
containers.add(container);
}
// Removing the sole listener from the container should free up a
// connection
containers.get(0).removeMessageListener(listener);
// verify we can now get a connection from the pool
RedisConnection connection = connectionFactory.getConnection();
connection.close();
}
@Test
public void testStopListening() throws InterruptedException {
// Grab all 8 connections from the pool.
MessageListener listener = new MessageListenerAdapter(handler);
for (int i = 0; i < 8; i++) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setBeanName("container" + i);
container.addMessageListener(listener, Arrays.asList(new ChannelTopic(CHANNEL)));
container.setTaskExecutor(new SyncTaskExecutor());
container.setSubscriptionExecutor(new SimpleAsyncTaskExecutor());
container.afterPropertiesSet();
container.start();
containers.add(container);
}
// Unsubscribe all listeners from all topics, freeing up a connection
containers.get(0).removeMessageListener(null, Arrays.asList(new Topic[] {}));
// verify we can now get a connection from the pool
RedisConnection connection = connectionFactory.getConnection();
connection.close();
}
}