diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 11f8d6cfb72..3eb06c8c7a2 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -244,11 +244,6 @@ jedis true - - io.lettuce - lettuce-core - true - org.springframework.amqp spring-rabbit diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index b5d5be58399..6bbd95a1ded 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -100,6 +100,11 @@ de.flapdoodle.embed.mongo true + + io.lettuce + lettuce-core + true + io.projectreactor.ipc reactor-netty @@ -481,11 +486,6 @@ jedis true - - io.lettuce - lettuce-core - true - org.liquibase liquibase-core diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java new file mode 100644 index 00000000000..27b13810a70 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java @@ -0,0 +1,120 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.redis; + +import java.net.UnknownHostException; + +import org.apache.commons.pool2.impl.GenericObjectPool; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPoolConfig; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisSentinelConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnection; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.util.StringUtils; + +/** + * Redis connection configuration using Jedis. + * + * @author Mark Paluch + * @author Stephane Nicoll + */ +@Configuration +@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) +class JedisConnectionConfiguration extends RedisConnectionConfiguration { + + private final RedisProperties properties; + + JedisConnectionConfiguration(RedisProperties properties, + ObjectProvider sentinelConfiguration, + ObjectProvider clusterConfiguration) { + super(properties, sentinelConfiguration, clusterConfiguration); + this.properties = properties; + } + + @Bean + @ConditionalOnMissingBean(RedisConnectionFactory.class) + public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException { + return applyProperties(createJedisConnectionFactory()); + } + + private JedisConnectionFactory applyProperties(JedisConnectionFactory factory) { + configureConnection(factory); + if (this.properties.isSsl()) { + factory.setUseSsl(true); + } + factory.setDatabase(this.properties.getDatabase()); + if (this.properties.getTimeout() > 0) { + factory.setTimeout(this.properties.getTimeout()); + } + return factory; + } + + private void configureConnection(JedisConnectionFactory factory) { + if (StringUtils.hasText(this.properties.getUrl())) { + configureConnectionFromUrl(factory); + } + else { + factory.setHostName(this.properties.getHost()); + factory.setPort(this.properties.getPort()); + if (this.properties.getPassword() != null) { + factory.setPassword(this.properties.getPassword()); + } + } + } + + private void configureConnectionFromUrl(JedisConnectionFactory factory) { + ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl()); + factory.setUseSsl(connectionInfo.isUseSsl()); + factory.setHostName(connectionInfo.getHostName()); + factory.setPort(connectionInfo.getPort()); + if (connectionInfo.getPassword() != null) { + factory.setPassword(connectionInfo.getPassword()); + } + } + + private JedisConnectionFactory createJedisConnectionFactory() { + RedisProperties.Pool pool = this.properties.getJedis().getPool(); + JedisPoolConfig poolConfig = pool != null + ? jedisPoolConfig(pool) : new JedisPoolConfig(); + + if (getSentinelConfig() != null) { + return new JedisConnectionFactory(getSentinelConfig(), poolConfig); + } + if (getClusterConfiguration() != null) { + return new JedisConnectionFactory(getClusterConfiguration(), poolConfig); + } + return new JedisConnectionFactory(poolConfig); + } + + private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxTotal(pool.getMaxActive()); + config.setMaxIdle(pool.getMaxIdle()); + config.setMinIdle(pool.getMinIdle()); + config.setMaxWaitMillis(pool.getMaxWait()); + return config; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java new file mode 100644 index 00000000000..d052b3ff134 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -0,0 +1,197 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.redis; + +import java.net.UnknownHostException; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.cluster.RedisClusterClient; +import io.lettuce.core.resource.ClientResources; +import io.lettuce.core.resource.DefaultClientResources; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisSentinelConfiguration; +import org.springframework.data.redis.connection.lettuce.DefaultLettucePool; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.util.StringUtils; + +/** + * Redis connection configuration using Lettuce. + * + * @author Mark Paluch + */ +@Configuration +@ConditionalOnClass({ GenericObjectPool.class, RedisClient.class, RedisClusterClient.class }) +class LettuceConnectionConfiguration extends RedisConnectionConfiguration { + + private final RedisProperties properties; + + LettuceConnectionConfiguration(RedisProperties properties, + ObjectProvider sentinelConfigurationProvider, + ObjectProvider clusterConfigurationProvider) { + super(properties, sentinelConfigurationProvider, clusterConfigurationProvider); + this.properties = properties; + } + + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(ClientResources.class) + public DefaultClientResources lettuceClientResources() { + return DefaultClientResources.create(); + } + + @Bean + @ConditionalOnMissingBean(RedisConnectionFactory.class) + public LettuceConnectionFactory redisConnectionFactory( + ClientResources clientResources) throws UnknownHostException { + return applyProperties(createLettuceConnectionFactory(clientResources)); + } + + private LettuceConnectionFactory applyProperties(LettuceConnectionFactory factory) { + configureConnection(factory); + if (this.properties.isSsl()) { + factory.setUseSsl(true); + } + if (this.properties.getLettuce() != null) { + RedisProperties.Lettuce lettuce = this.properties.getLettuce(); + if (lettuce.getShutdownTimeout() >= 0) { + factory.setShutdownTimeout( + this.properties.getLettuce().getShutdownTimeout()); + } + } + return factory; + } + + private void configureConnection(LettuceConnectionFactory factory) { + if (StringUtils.hasText(this.properties.getUrl())) { + configureConnectionFromUrl(factory); + } + else { + factory.setHostName(this.properties.getHost()); + factory.setPort(this.properties.getPort()); + if (this.properties.getPassword() != null) { + factory.setPassword(this.properties.getPassword()); + } + factory.setDatabase(this.properties.getDatabase()); + if (this.properties.getTimeout() > 0) { + factory.setTimeout(this.properties.getTimeout()); + } + } + } + + private void configureConnectionFromUrl(LettuceConnectionFactory factory) { + ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl()); + factory.setUseSsl(connectionInfo.isUseSsl()); + factory.setHostName(connectionInfo.getHostName()); + factory.setPort(connectionInfo.getPort()); + if (connectionInfo.getPassword() != null) { + factory.setPassword(connectionInfo.getPassword()); + } + } + + private DefaultLettucePool applyProperties(DefaultLettucePool pool) { + if (StringUtils.hasText(this.properties.getUrl())) { + configureConnectionFromUrl(pool); + } + else { + pool.setHostName(this.properties.getHost()); + pool.setPort(this.properties.getPort()); + if (this.properties.getPassword() != null) { + pool.setPassword(this.properties.getPassword()); + } + pool.setDatabase(this.properties.getDatabase()); + } + if (this.properties.getTimeout() > 0) { + pool.setTimeout(this.properties.getTimeout()); + } + pool.afterPropertiesSet(); + return pool; + } + + private void configureConnectionFromUrl(DefaultLettucePool lettucePool) { + ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl()); + lettucePool.setHostName(connectionInfo.getHostName()); + lettucePool.setPort(connectionInfo.getPort()); + if (connectionInfo.getPassword() != null) { + lettucePool.setPassword(connectionInfo.getPassword()); + } + } + + private LettuceConnectionFactory createLettuceConnectionFactory( + ClientResources clientResources) { + + if (getSentinelConfig() != null) { + if (this.properties.getLettuce() != null + && this.properties.getLettuce().getPool() != null) { + DefaultLettucePool lettucePool = new DefaultLettucePool( + getSentinelConfig()); + return new LettuceConnectionFactory(applyProperties( + applyClientResources(lettucePool, clientResources))); + } + return applyClientResources( + new LettuceConnectionFactory(getSentinelConfig()), + clientResources); + } + + if (getClusterConfiguration() != null) { + return applyClientResources( + new LettuceConnectionFactory(getClusterConfiguration()), + clientResources); + } + + if (this.properties.getLettuce() != null + && this.properties.getLettuce().getPool() != null) { + GenericObjectPoolConfig config = lettucePoolConfig( + this.properties.getLettuce().getPool()); + DefaultLettucePool lettucePool = new DefaultLettucePool( + this.properties.getHost(), this.properties.getPort(), config); + return new LettuceConnectionFactory(applyProperties( + applyClientResources(lettucePool, clientResources))); + } + + return applyClientResources(new LettuceConnectionFactory(), clientResources); + } + + private DefaultLettucePool applyClientResources(DefaultLettucePool lettucePool, + ClientResources clientResources) { + lettucePool.setClientResources(clientResources); + return lettucePool; + } + + private LettuceConnectionFactory applyClientResources( + LettuceConnectionFactory factory, ClientResources clientResources) { + factory.setClientResources(clientResources); + return factory; + } + + private GenericObjectPoolConfig lettucePoolConfig(RedisProperties.Pool props) { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(props.getMaxActive()); + config.setMaxIdle(props.getMaxIdle()); + config.setMinIdle(props.getMinIdle()); + config.setMaxWaitMillis(props.getMaxWait()); + return config; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java index e19139a5f0a..629cf2d3811 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java @@ -16,44 +16,19 @@ package org.springframework.boot.autoconfigure.data.redis; -import java.net.URI; -import java.net.URISyntaxException; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; -import io.lettuce.core.RedisClient; -import io.lettuce.core.cluster.RedisClusterClient; -import io.lettuce.core.resource.ClientResources; -import io.lettuce.core.resource.DefaultClientResources; -import org.apache.commons.pool2.impl.GenericObjectPool; -import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPoolConfig; - -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Cluster; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.context.annotation.Import; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.RedisNode; -import org.springframework.data.redis.connection.RedisSentinelConfiguration; -import org.springframework.data.redis.connection.jedis.JedisConnection; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import org.springframework.data.redis.connection.lettuce.DefaultLettucePool; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; /** * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support. @@ -71,393 +46,28 @@ import org.springframework.util.StringUtils; @Configuration @ConditionalOnClass({ RedisOperations.class }) @EnableConfigurationProperties(RedisProperties.class) +@Import({ LettuceConnectionConfiguration.class, + JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { - /** - * Jedis Redis connection configuration. - */ - @Configuration - @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) - protected static class JedisRedisConnectionConfiguration - extends RedisBaseConfiguration { - - private final RedisProperties properties; - - public JedisRedisConnectionConfiguration(RedisProperties properties, - ObjectProvider sentinelConfiguration, - ObjectProvider clusterConfiguration) { - super(properties, sentinelConfiguration, clusterConfiguration); - this.properties = properties; - } - - @Bean - @ConditionalOnMissingBean(RedisConnectionFactory.class) - public JedisConnectionFactory redisConnectionFactory() - throws UnknownHostException { - return applyProperties(createJedisConnectionFactory()); - } - - protected final JedisConnectionFactory applyProperties( - JedisConnectionFactory factory) { - configureConnection(factory); - if (this.properties.isSsl()) { - factory.setUseSsl(true); - } - factory.setDatabase(this.properties.getDatabase()); - if (this.properties.getTimeout() > 0) { - factory.setTimeout(this.properties.getTimeout()); - } - return factory; - } - - private void configureConnection(JedisConnectionFactory factory) { - if (StringUtils.hasText(this.properties.getUrl())) { - configureConnectionFromUrl(factory); - } - else { - factory.setHostName(this.properties.getHost()); - factory.setPort(this.properties.getPort()); - if (this.properties.getPassword() != null) { - factory.setPassword(this.properties.getPassword()); - } - } - } - - private void configureConnectionFromUrl(JedisConnectionFactory factory) { - String url = this.properties.getUrl(); - if (url.startsWith("rediss://")) { - factory.setUseSsl(true); - } - try { - URI uri = new URI(url); - factory.setHostName(uri.getHost()); - factory.setPort(uri.getPort()); - if (uri.getUserInfo() != null) { - String password = uri.getUserInfo(); - int index = password.lastIndexOf(":"); - if (index >= 0) { - password = password.substring(index + 1); - } - factory.setPassword(password); - } - } - catch (URISyntaxException ex) { - throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url, - ex); - } - } - - private JedisConnectionFactory createJedisConnectionFactory() { - JedisPoolConfig poolConfig = this.properties.getPool() != null - ? jedisPoolConfig() : new JedisPoolConfig(); - - if (getSentinelConfig() != null) { - return new JedisConnectionFactory(getSentinelConfig(), poolConfig); - } - if (getClusterConfiguration() != null) { - return new JedisConnectionFactory(getClusterConfiguration(), poolConfig); - } - return new JedisConnectionFactory(poolConfig); - } - - private JedisPoolConfig jedisPoolConfig() { - JedisPoolConfig config = new JedisPoolConfig(); - RedisProperties.Pool props = this.properties.getPool(); - config.setMaxTotal(props.getMaxActive()); - config.setMaxIdle(props.getMaxIdle()); - config.setMinIdle(props.getMinIdle()); - config.setMaxWaitMillis(props.getMaxWait()); - return config; - } - + @Bean + @ConditionalOnMissingBean(name = "redisTemplate") + public RedisTemplate redisTemplate( + RedisConnectionFactory redisConnectionFactory) + throws UnknownHostException { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory); + return template; } - /** - * Lettuce Redis connection configuration. - */ - @Configuration - @ConditionalOnClass({ GenericObjectPool.class, RedisClient.class, - RedisClusterClient.class }) - protected static class LettuceRedisConnectionConfiguration - extends RedisBaseConfiguration { - - private final RedisProperties properties; - - public LettuceRedisConnectionConfiguration(RedisProperties properties, - ObjectProvider sentinelConfigurationProvider, - ObjectProvider clusterConfigurationProvider) { - super(properties, sentinelConfigurationProvider, - clusterConfigurationProvider); - this.properties = properties; - } - - @Bean(destroyMethod = "shutdown") - @ConditionalOnMissingBean(ClientResources.class) - public DefaultClientResources lettuceClientResources() { - return DefaultClientResources.create(); - } - - @Bean - @ConditionalOnMissingBean(RedisConnectionFactory.class) - public LettuceConnectionFactory redisConnectionFactory( - ClientResources clientResources) throws UnknownHostException { - return applyProperties(createLettuceConnectionFactory(clientResources)); - } - - protected final LettuceConnectionFactory applyProperties( - LettuceConnectionFactory factory) { - configureConnection(factory); - if (this.properties.isSsl()) { - factory.setUseSsl(true); - } - if (this.properties.getLettuce() != null) { - Lettuce lettuce = this.properties.getLettuce(); - if (lettuce.getShutdownTimeout() >= 0) { - factory.setShutdownTimeout( - this.properties.getLettuce().getShutdownTimeout()); - } - } - return factory; - } - - private void configureConnection(LettuceConnectionFactory factory) { - if (StringUtils.hasText(this.properties.getUrl())) { - configureConnectionFromUrl(factory); - } - else { - factory.setHostName(this.properties.getHost()); - factory.setPort(this.properties.getPort()); - if (this.properties.getPassword() != null) { - factory.setPassword(this.properties.getPassword()); - } - factory.setDatabase(this.properties.getDatabase()); - if (this.properties.getTimeout() > 0) { - factory.setTimeout(this.properties.getTimeout()); - } - } - } - - private void configureConnectionFromUrl(LettuceConnectionFactory factory) { - String url = this.properties.getUrl(); - if (url.startsWith("rediss://")) { - factory.setUseSsl(true); - } - try { - URI uri = new URI(url); - factory.setHostName(uri.getHost()); - factory.setPort(uri.getPort()); - if (uri.getUserInfo() != null) { - String password = uri.getUserInfo(); - int index = password.lastIndexOf(":"); - if (index >= 0) { - password = password.substring(index + 1); - } - factory.setPassword(password); - } - } - catch (URISyntaxException ex) { - throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url, - ex); - } - } - - protected final DefaultLettucePool applyProperties(DefaultLettucePool pool) { - if (StringUtils.hasText(this.properties.getUrl())) { - configureConnectionFromUrl(pool); - } - else { - pool.setHostName(this.properties.getHost()); - pool.setPort(this.properties.getPort()); - if (this.properties.getPassword() != null) { - pool.setPassword(this.properties.getPassword()); - } - pool.setDatabase(this.properties.getDatabase()); - } - if (this.properties.getTimeout() > 0) { - pool.setTimeout(this.properties.getTimeout()); - } - pool.afterPropertiesSet(); - return pool; - } - - private void configureConnectionFromUrl(DefaultLettucePool lettucePool) { - String url = this.properties.getUrl(); - try { - URI uri = new URI(url); - lettucePool.setHostName(uri.getHost()); - lettucePool.setPort(uri.getPort()); - if (uri.getUserInfo() != null) { - String password = uri.getUserInfo(); - int index = password.lastIndexOf(":"); - if (index >= 0) { - password = password.substring(index + 1); - } - lettucePool.setPassword(password); - } - } - catch (URISyntaxException ex) { - throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url, - ex); - } - } - - private LettuceConnectionFactory createLettuceConnectionFactory( - ClientResources clientResources) { - - if (getSentinelConfig() != null) { - if (this.properties.getLettuce() != null - && this.properties.getLettuce().getPool() != null) { - DefaultLettucePool lettucePool = new DefaultLettucePool( - getSentinelConfig()); - return new LettuceConnectionFactory(applyProperties( - applyClientResources(lettucePool, clientResources))); - } - return applyClientResources( - new LettuceConnectionFactory(getSentinelConfig()), - clientResources); - } - - if (getClusterConfiguration() != null) { - return applyClientResources( - new LettuceConnectionFactory(getClusterConfiguration()), - clientResources); - } - - if (this.properties.getLettuce() != null - && this.properties.getLettuce().getPool() != null) { - GenericObjectPoolConfig config = lettucePoolConfig( - this.properties.getLettuce().getPool()); - DefaultLettucePool lettucePool = new DefaultLettucePool( - this.properties.getHost(), this.properties.getPort(), config); - return new LettuceConnectionFactory(applyProperties( - applyClientResources(lettucePool, clientResources))); - } - - return applyClientResources(new LettuceConnectionFactory(), clientResources); - } - - private DefaultLettucePool applyClientResources(DefaultLettucePool lettucePool, - ClientResources clientResources) { - lettucePool.setClientResources(clientResources); - return lettucePool; - } - - private LettuceConnectionFactory applyClientResources( - LettuceConnectionFactory factory, ClientResources clientResources) { - factory.setClientResources(clientResources); - return factory; - } - - private GenericObjectPoolConfig lettucePoolConfig(RedisProperties.Pool props) { - GenericObjectPoolConfig config = new GenericObjectPoolConfig(); - config.setMaxTotal(props.getMaxActive()); - config.setMaxIdle(props.getMaxIdle()); - config.setMinIdle(props.getMinIdle()); - config.setMaxWaitMillis(props.getMaxWait()); - return config; - } - - } - - protected abstract static class RedisBaseConfiguration { - - private final RedisProperties properties; - - private final RedisSentinelConfiguration sentinelConfiguration; - - private final RedisClusterConfiguration clusterConfiguration; - - protected RedisBaseConfiguration(RedisProperties properties, - ObjectProvider sentinelConfigurationProvider, - ObjectProvider clusterConfigurationProvider) { - this.properties = properties; - this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable(); - this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable(); - } - - protected final RedisSentinelConfiguration getSentinelConfig() { - if (this.sentinelConfiguration != null) { - return this.sentinelConfiguration; - } - - Sentinel sentinelProperties = this.properties.getSentinel(); - if (sentinelProperties != null) { - RedisSentinelConfiguration config = new RedisSentinelConfiguration(); - config.master(sentinelProperties.getMaster()); - config.setSentinels(createSentinels(sentinelProperties)); - return config; - } - return null; - } - - /** - * Create a {@link RedisClusterConfiguration} if necessary. - * @return {@literal null} if no cluster settings are set. - */ - protected final RedisClusterConfiguration getClusterConfiguration() { - if (this.clusterConfiguration != null) { - return this.clusterConfiguration; - } - - if (this.properties.getCluster() == null) { - return null; - } - - Cluster clusterProperties = this.properties.getCluster(); - RedisClusterConfiguration config = new RedisClusterConfiguration( - clusterProperties.getNodes()); - - if (clusterProperties.getMaxRedirects() != null) { - config.setMaxRedirects(clusterProperties.getMaxRedirects()); - } - return config; - } - - private List createSentinels(Sentinel sentinel) { - List nodes = new ArrayList<>(); - for (String node : StringUtils - .commaDelimitedListToStringArray(sentinel.getNodes())) { - try { - String[] parts = StringUtils.split(node, ":"); - Assert.state(parts.length == 2, "Must be defined as 'host:port'"); - nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1]))); - } - catch (RuntimeException ex) { - throw new IllegalStateException( - "Invalid redis sentinel " + "property '" + node + "'", ex); - } - } - return nodes; - } - - } - - /** - * Standard Redis configuration. - */ - @Configuration - protected static class RedisConfiguration { - - @Bean - @ConditionalOnMissingBean(name = "redisTemplate") - public RedisTemplate redisTemplate( - RedisConnectionFactory redisConnectionFactory) - throws UnknownHostException { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(redisConnectionFactory); - return template; - } - - @Bean - @ConditionalOnMissingBean(StringRedisTemplate.class) - public StringRedisTemplate stringRedisTemplate( - RedisConnectionFactory redisConnectionFactory) - throws UnknownHostException { - StringRedisTemplate template = new StringRedisTemplate(); - template.setConnectionFactory(redisConnectionFactory); - return template; - } - + @Bean + @ConditionalOnMissingBean(StringRedisTemplate.class) + public StringRedisTemplate stringRedisTemplate( + RedisConnectionFactory redisConnectionFactory) + throws UnknownHostException { + StringRedisTemplate template = new StringRedisTemplate(); + template.setConnectionFactory(redisConnectionFactory); + return template; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java new file mode 100644 index 00000000000..b8ae36329f2 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java @@ -0,0 +1,153 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.redis; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.RedisNode; +import org.springframework.data.redis.connection.RedisSentinelConfiguration; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Base Redis connection configuration. + * + * @author Mark Paluch + * @author Stephane Nicoll + */ +abstract class RedisConnectionConfiguration { + + private final RedisProperties properties; + + private final RedisSentinelConfiguration sentinelConfiguration; + + private final RedisClusterConfiguration clusterConfiguration; + + protected RedisConnectionConfiguration(RedisProperties properties, + ObjectProvider sentinelConfigurationProvider, + ObjectProvider clusterConfigurationProvider) { + this.properties = properties; + this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable(); + this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable(); + } + + protected final RedisSentinelConfiguration getSentinelConfig() { + if (this.sentinelConfiguration != null) { + return this.sentinelConfiguration; + } + RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel(); + if (sentinelProperties != null) { + RedisSentinelConfiguration config = new RedisSentinelConfiguration(); + config.master(sentinelProperties.getMaster()); + config.setSentinels(createSentinels(sentinelProperties)); + return config; + } + return null; + } + + /** + * Create a {@link RedisClusterConfiguration} if necessary. + * @return {@literal null} if no cluster settings are set. + */ + protected final RedisClusterConfiguration getClusterConfiguration() { + if (this.clusterConfiguration != null) { + return this.clusterConfiguration; + } + if (this.properties.getCluster() == null) { + return null; + } + RedisProperties.Cluster clusterProperties = this.properties.getCluster(); + RedisClusterConfiguration config = new RedisClusterConfiguration( + clusterProperties.getNodes()); + if (clusterProperties.getMaxRedirects() != null) { + config.setMaxRedirects(clusterProperties.getMaxRedirects()); + } + return config; + } + + private List createSentinels(RedisProperties.Sentinel sentinel) { + List nodes = new ArrayList<>(); + for (String node : StringUtils + .commaDelimitedListToStringArray(sentinel.getNodes())) { + try { + String[] parts = StringUtils.split(node, ":"); + Assert.state(parts.length == 2, "Must be defined as 'host:port'"); + nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1]))); + } + catch (RuntimeException ex) { + throw new IllegalStateException( + "Invalid redis sentinel " + "property '" + node + "'", ex); + } + } + return nodes; + } + + protected ConnectionInfo parseUrl(String url) { + try { + URI uri = new URI(url); + boolean useSsl = (url.startsWith("rediss://")); + String password = null; + if (uri.getUserInfo() != null) { + password = uri.getUserInfo(); + int index = password.lastIndexOf(":"); + if (index >= 0) { + password = password.substring(index + 1); + } + } + return new ConnectionInfo(uri, useSsl, password); + } + catch (URISyntaxException ex) { + throw new IllegalArgumentException("Malformed url '" + url + "'", ex); + } + } + + protected static class ConnectionInfo { + + private final URI uri; + private final boolean useSsl; + private final String password; + + public ConnectionInfo(URI uri, boolean useSsl, String password) { + this.uri = uri; + this.useSsl = useSsl; + this.password = password; + } + + public boolean isUseSsl() { + return this.useSsl; + } + + public String getHostName() { + return this.uri.getHost(); + } + + public int getPort() { + return this.uri.getPort(); + } + + public String getPassword() { + return this.password; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java index e0d9de2bdfe..f9b34bbb151 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java @@ -19,7 +19,6 @@ package org.springframework.boot.autoconfigure.data.redis; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; /** * Configuration properties for Redis. @@ -29,6 +28,7 @@ import org.springframework.boot.context.properties.DeprecatedConfigurationProper * @author Eddú Meléndez * @author Marco Aust * @author Mark Paluch + * @author Stephane Nicoll */ @ConfigurationProperties(prefix = "spring.redis") public class RedisProperties { @@ -68,21 +68,13 @@ public class RedisProperties { */ private int timeout; - private Pool pool; - private Sentinel sentinel; private Cluster cluster; - /** - * Jedis client properties. - */ - private Jedis jedis; + private final Jedis jedis = new Jedis(); - /** - * Lettuce client properties. - */ - private Lettuce lettuce; + private final Lettuce lettuce = new Lettuce(); public int getDatabase() { return this.database; @@ -148,15 +140,6 @@ public class RedisProperties { this.sentinel = sentinel; } - @DeprecatedConfigurationProperty(reason = "Moved to client-specific properties", replacement = "spring.redis.jedis.pool") - public Pool getPool() { - return this.pool; - } - - public void setPool(Pool pool) { - this.pool = pool; - } - public Cluster getCluster() { return this.cluster; } @@ -166,19 +149,11 @@ public class RedisProperties { } public Jedis getJedis() { - return jedis; - } - - public void setJedis(Jedis jedis) { - this.jedis = jedis; + return this.jedis; } public Lettuce getLettuce() { - return lettuce; - } - - public void setLettuce(Lettuce lettuce) { - this.lettuce = lettuce; + return this.lettuce; } /** @@ -339,7 +314,7 @@ public class RedisProperties { public static class Lettuce { /** - * Shutdown timeout in milliseconds for lettuce. + * Shutdown timeout in milliseconds. */ private int shutdownTimeout = 2000; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/LettuceRedisAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/LettuceRedisAutoConfigurationTests.java deleted file mode 100644 index d0a9ff9f088..00000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/LettuceRedisAutoConfigurationTests.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2012-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.boot.autoconfigure.data.redis; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import io.lettuce.core.RedisClient; -import io.lettuce.core.RedisURI; -import io.lettuce.core.api.StatefulRedisConnection; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.test.util.EnvironmentTestUtils; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.data.redis.connection.lettuce.DefaultLettucePool; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.StringUtils; - -import static org.assertj.core.api.Assertions.*; - -/** - * Tests for {@link RedisAutoConfiguration} using Lettuce as client. - * - * @author Mark Paluch - */ -public class LettuceRedisAutoConfigurationTests { - - private AnnotationConfigApplicationContext context; - - @Before - public void setup() { - this.context = new AnnotationConfigApplicationContext(); - } - - @After - public void close() { - if (this.context != null) { - this.context.close(); - } - } - - @Test - public void testOverrideRedisConfiguration() throws Exception { - load("spring.redis.host:foo", "spring.redis.database:1"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName()) - .isEqualTo("foo"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getDatabase()) - .isEqualTo(1); - } - - @Test - public void testOverrideUrlRedisConfiguration() throws Exception { - load("spring.redis.host:foo", "spring.redis.password:xyz", - "spring.redis.port:1000", "spring.redis.ssl:true", - "spring.redis.url:redis://user:password@example:33"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName()) - .isEqualTo("example"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getPort()) - .isEqualTo(33); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getPassword()) - .isEqualTo("password"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).isUseSsl()) - .isEqualTo(true); - } - - @Test - public void testSslRedisConfiguration() throws Exception { - load("spring.redis.host:foo", "spring.redis.ssl:true"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName()) - .isEqualTo("foo"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).isUseSsl()) - .isTrue(); - } - - @Test - public void testRedisConfigurationWithPool() throws Exception { - load("spring.redis.host:foo", "spring.redis.lettuce.pool.max-idle:1"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName()) - .isEqualTo("foo"); - assertThat(getDefaultLettucePool( - this.context.getBean(LettuceConnectionFactory.class)).getHostName()) - .isEqualTo("foo"); - assertThat(getDefaultLettucePool( - this.context.getBean(LettuceConnectionFactory.class)).getPoolConfig() - .getMaxIdle()).isEqualTo(1); - } - - @Test - public void testRedisConfigurationWithTimeout() throws Exception { - load("spring.redis.host:foo", "spring.redis.timeout:100"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName()) - .isEqualTo("foo"); - assertThat(this.context.getBean(LettuceConnectionFactory.class).getTimeout()) - .isEqualTo(100); - } - - @Test - public void testRedisConfigurationWithSentinel() throws Exception { - List sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380"); - if (isAtLeastOneNodeAvailable(sentinels)) { - load("spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:" - + StringUtils.collectionToCommaDelimitedString(sentinels)); - assertThat(this.context.getBean(LettuceConnectionFactory.class) - .isRedisSentinelAware()).isTrue(); - } - } - - @Test - public void testRedisConfigurationWithCluster() throws Exception { - List clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380"); - load("spring.redis.cluster.nodes[0]:" + clusterNodes.get(0), - "spring.redis.cluster.nodes[1]:" + clusterNodes.get(1)); - assertThat(this.context.getBean(LettuceConnectionFactory.class) - .getClusterConnection()).isNotNull(); - } - - private DefaultLettucePool getDefaultLettucePool(LettuceConnectionFactory factory) { - return (DefaultLettucePool) ReflectionTestUtils.getField(factory, "pool"); - } - - private boolean isAtLeastOneNodeAvailable(List nodes) { - for (String node : nodes) { - if (isAvailable(node)) { - return true; - } - } - - return false; - } - - private boolean isAvailable(String node) { - RedisClient redisClient = null; - try { - String[] hostAndPort = node.split(":"); - redisClient = RedisClient.create(new RedisURI(hostAndPort[0], - Integer.valueOf(hostAndPort[1]), 10, TimeUnit.SECONDS)); - StatefulRedisConnection connection = redisClient.connect(); - connection.sync().ping(); - connection.close(); - return true; - } - catch (Exception ex) { - return false; - } - finally { - if (redisClient != null) { - try { - redisClient.shutdown(0, 0, TimeUnit.SECONDS); - } - catch (Exception ex) { - // Continue - } - } - } - } - - private void load(String... environment) { - this.context = doLoad(environment); - } - - private AnnotationConfigApplicationContext doLoad(String... environment) { - - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - EnvironmentTestUtils.addEnvironment(applicationContext, environment); - applicationContext.register( - RedisAutoConfiguration.LettuceRedisConnectionConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, - EnableRedisPropertiesConfiguration.class); - applicationContext.refresh(); - return applicationContext; - } - - @EnableConfigurationProperties(RedisProperties.class) - private static class EnableRedisPropertiesConfiguration { - - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java new file mode 100644 index 00000000000..e4e1d23c9ac --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java @@ -0,0 +1,158 @@ +/* + * Copyright 2012-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.boot.autoconfigure.data.redis; + +import java.util.Arrays; +import java.util.List; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import redis.clients.jedis.Jedis; + +import org.springframework.boot.junit.runner.classpath.ClassPathExclusions; +import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.util.StringUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link RedisAutoConfiguration} when Lettuce is not on the classpath. + * + * @author Mark Paluch + * @author Stephane Nicoll + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions("lettuce-core-*.jar") +public class RedisAutoConfigurationJedisTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void testOverrideRedisConfiguration() throws Exception { + load("spring.redis.host:foo", "spring.redis.database:1"); + JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getDatabase()).isEqualTo(1); + assertThat(cf.getPassword()).isNull(); + assertThat(cf.isUseSsl()).isFalse(); + } + + @Test + public void testOverrideUrlRedisConfiguration() throws Exception { + load("spring.redis.host:foo", "spring.redis.password:xyz", + "spring.redis.port:1000", "spring.redis.ssl:true", + "spring.redis.url:redis://user:password@example:33"); + JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); + } + + @Test + public void testRedisConfigurationWithPool() throws Exception { + load("spring.redis.host:foo", "spring.redis.jedis.pool.max-idle:1"); + JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getPoolConfig().getMaxIdle()).isEqualTo(1); + } + + @Test + public void testRedisConfigurationWithTimeout() throws Exception { + load("spring.redis.host:foo", "spring.redis.timeout:100"); + JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getTimeout()).isEqualTo(100); + } + + @Test + public void testRedisConfigurationWithSentinel() throws Exception { + List sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380"); + if (isAtLeastOneNodeAvailable(sentinels)) { + load("spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:" + + StringUtils.collectionToCommaDelimitedString(sentinels)); + assertThat(this.context.getBean(JedisConnectionFactory.class) + .isRedisSentinelAware()).isTrue(); + } + } + + @Test + public void testRedisConfigurationWithCluster() throws Exception { + List clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380"); + if (isAtLeastOneNodeAvailable(clusterNodes)) { + load("spring.redis.cluster.nodes[0]:" + clusterNodes.get(0), + "spring.redis.cluster.nodes[1]:" + clusterNodes.get(1)); + assertThat(this.context.getBean(JedisConnectionFactory.class) + .getClusterConnection()).isNotNull(); + } + } + + private void load(String... environment) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(ctx, environment); + ctx.register(RedisAutoConfiguration.class); + ctx.refresh(); + this.context = ctx; + } + + private boolean isAtLeastOneNodeAvailable(List nodes) { + for (String node : nodes) { + if (isAvailable(node)) { + return true; + } + } + + return false; + } + + private boolean isAvailable(String node) { + Jedis jedis = null; + try { + String[] hostAndPort = node.split(":"); + jedis = new Jedis(hostAndPort[0], Integer.valueOf(hostAndPort[1])); + jedis.connect(); + jedis.ping(); + return true; + } + catch (Exception ex) { + return false; + } + finally { + if (jedis != null) { + try { + jedis.disconnect(); + jedis.close(); + } + catch (Exception ex) { + // Continue + } + } + } + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java index 165e5cd9a0c..90202137cee 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java @@ -18,19 +18,21 @@ package org.springframework.boot.autoconfigure.data.redis; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; +import io.lettuce.core.RedisClient; +import io.lettuce.core.RedisURI; +import io.lettuce.core.api.StatefulRedisConnection; import org.junit.After; -import org.junit.Before; import org.junit.Test; -import redis.clients.jedis.Jedis; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.DefaultLettucePool; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -44,16 +46,12 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Eddú Meléndez * @author Marco Aust * @author Mark Paluch + * @author Stephane Nicoll */ public class RedisAutoConfigurationTests { private AnnotationConfigApplicationContext context; - @Before - public void setup() { - this.context = new AnnotationConfigApplicationContext(); - } - @After public void close() { if (this.context != null) { @@ -62,7 +60,7 @@ public class RedisAutoConfigurationTests { } @Test - public void testDefaultRedisConfiguration() throws Exception { + public void testDefaultRedisConfiguration() { load(); assertThat(this.context.getBean("redisTemplate", RedisOperations.class)) .isNotNull(); @@ -70,45 +68,42 @@ public class RedisAutoConfigurationTests { } @Test - public void testOverrideRedisConfiguration() throws Exception { + public void testOverrideRedisConfiguration() { load("spring.redis.host:foo", "spring.redis.database:1"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName()) - .isEqualTo("foo"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getDatabase()) - .isEqualTo(1); + LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getDatabase()).isEqualTo(1); + assertThat(cf.getPassword()).isNull(); + assertThat(cf.isUseSsl()).isFalse(); } @Test - public void testOverrideUrlRedisConfiguration() throws Exception { + public void testOverrideUrlRedisConfiguration() { load("spring.redis.host:foo", "spring.redis.password:xyz", "spring.redis.port:1000", "spring.redis.ssl:true", "spring.redis.url:redis://user:password@example:33"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName()) - .isEqualTo("example"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getPort()) - .isEqualTo(33); - assertThat(this.context.getBean(JedisConnectionFactory.class).getPassword()) - .isEqualTo("password"); - assertThat(this.context.getBean(JedisConnectionFactory.class).isUseSsl()) - .isEqualTo(true); + LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); } @Test public void testRedisConfigurationWithPool() throws Exception { - load("spring.redis.host:foo", "spring.redis.jedis.pool.max-idle:1"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName()) - .isEqualTo("foo"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getPoolConfig() - .getMaxIdle()).isEqualTo(1); + load("spring.redis.host:foo", "spring.redis.lettuce.pool.max-idle:1"); + LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(getDefaultLettucePool(cf).getHostName()).isEqualTo("foo"); + assertThat(getDefaultLettucePool(cf).getPoolConfig().getMaxIdle()).isEqualTo(1); } @Test public void testRedisConfigurationWithTimeout() throws Exception { load("spring.redis.host:foo", "spring.redis.timeout:100"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName()) - .isEqualTo("foo"); - assertThat(this.context.getBean(JedisConnectionFactory.class).getTimeout()) - .isEqualTo(100); + LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getTimeout()).isEqualTo(100); } @Test @@ -117,7 +112,7 @@ public class RedisAutoConfigurationTests { if (isAtLeastOneNodeAvailable(sentinels)) { load("spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:" + StringUtils.collectionToCommaDelimitedString(sentinels)); - assertThat(this.context.getBean(JedisConnectionFactory.class) + assertThat(this.context.getBean(LettuceConnectionFactory.class) .isRedisSentinelAware()).isTrue(); } } @@ -125,12 +120,14 @@ public class RedisAutoConfigurationTests { @Test public void testRedisConfigurationWithCluster() throws Exception { List clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380"); - if (isAtLeastOneNodeAvailable(clusterNodes)) { - load("spring.redis.cluster.nodes[0]:" + clusterNodes.get(0), - "spring.redis.cluster.nodes[1]:" + clusterNodes.get(1)); - assertThat(this.context.getBean(JedisConnectionFactory.class) - .getClusterConnection()).isNotNull(); - } + load("spring.redis.cluster.nodes[0]:" + clusterNodes.get(0), + "spring.redis.cluster.nodes[1]:" + clusterNodes.get(1)); + assertThat(this.context.getBean(LettuceConnectionFactory.class) + .getClusterConnection()).isNotNull(); + } + + private DefaultLettucePool getDefaultLettucePool(LettuceConnectionFactory factory) { + return (DefaultLettucePool) ReflectionTestUtils.getField(factory, "pool"); } private boolean isAtLeastOneNodeAvailable(List nodes) { @@ -144,22 +141,23 @@ public class RedisAutoConfigurationTests { } private boolean isAvailable(String node) { - Jedis jedis = null; + RedisClient redisClient = null; try { String[] hostAndPort = node.split(":"); - jedis = new Jedis(hostAndPort[0], Integer.valueOf(hostAndPort[1])); - jedis.connect(); - jedis.ping(); + redisClient = RedisClient.create(new RedisURI(hostAndPort[0], + Integer.valueOf(hostAndPort[1]), 10, TimeUnit.SECONDS)); + StatefulRedisConnection connection = redisClient.connect(); + connection.sync().ping(); + connection.close(); return true; } catch (Exception ex) { return false; } finally { - if (jedis != null) { + if (redisClient != null) { try { - jedis.disconnect(); - jedis.close(); + redisClient.shutdown(0, 0, TimeUnit.SECONDS); } catch (Exception ex) { // Continue @@ -169,21 +167,11 @@ public class RedisAutoConfigurationTests { } private void load(String... environment) { - this.context = doLoad(environment); - } - - private AnnotationConfigApplicationContext doLoad(String... environment) { - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - EnvironmentTestUtils.addEnvironment(applicationContext, environment); - applicationContext.register(RedisAutoConfiguration.JedisRedisConnectionConfiguration.class, RedisAutoConfiguration.RedisConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, EnableRedisPropertiesConfiguration.class); - applicationContext.refresh(); - return applicationContext; - } - - @EnableConfigurationProperties(RedisProperties.class) - private static class EnableRedisPropertiesConfiguration { - + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(ctx, environment); + ctx.register(RedisAutoConfiguration.class); + ctx.refresh(); + this.context = ctx; } } diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 7d061aa46dd..589ed7e9842 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -125,7 +125,6 @@ 4.12 5.0.0.BUILD-SNAPSHOT 3.5.3 - 5.0.0.BUILD-SNAPSHOT 2.8.2 1.2.3 1.16.16 @@ -571,11 +570,6 @@ antlr ${antlr2.version} - - io.lettuce - lettuce-core - ${lettuce.version} - ch.qos.logback logback-access @@ -831,6 +825,11 @@ metrics-servlets ${dropwizard-metrics.version} + + io.lettuce + lettuce-core + ${lettuce.version} + io.netty netty-bom @@ -2338,11 +2337,6 @@ jedis ${jedis.version} - - io.lettuce - lettuce-core - ${lettuce.version} - wsdl4j wsdl4j diff --git a/spring-boot-docs/pom.xml b/spring-boot-docs/pom.xml index 94fb2746e71..564a47d8f7b 100644 --- a/spring-boot-docs/pom.xml +++ b/spring-boot-docs/pom.xml @@ -172,6 +172,11 @@ metrics-core true + + io.lettuce + lettuce-core + true + io.projectreactor.ipc reactor-netty @@ -748,11 +753,6 @@ jedis true - - io.lettuce - lettuce-core - true - org.springframework.boot diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 0d40b09b2f0..0d3c57fac00 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -850,38 +850,28 @@ content into your application; rather pick only the properties that you need. spring.redis.database=0 # Database index used by the connection factory. spring.redis.url= # Connection URL, will override host, port and password (user will be ignored), e.g. redis://user:password@example.com:6379 spring.redis.host=localhost # Redis server host. - spring.redis.password= # Login password of the redis server. - spring.redis.ssl=false # Enable SSL support. spring.redis.jedis.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit. spring.redis.jedis.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections. spring.redis.jedis.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely. spring.redis.jedis.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive. - spring.redis.port=6379 # Redis server port. - spring.redis.sentinel.master= # Name of Redis server. - spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs. - spring.redis.timeout=0 # Connection timeout in milliseconds. - spring.redis.ssl.enabled=false # Enable SSL support. - spring.redis.ssl.verify-peer=true # Enable SSL peer verification. - spring.redis.ssl.start-tls=false # Enable StartTLS support. - - # REDIS JEDIS DRIVER - spring.redis.jedis.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit. - spring.redis.jedis.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections. - spring.redis.jedis.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely. - spring.redis.jedis.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive. - - # REDIS LETTUCE DRIVER spring.redis.lettuce.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit. spring.redis.lettuce.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections. spring.redis.lettuce.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely. spring.redis.lettuce.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive. spring.redis.lettuce.shutdown-timeout=2000 # Shutdown timeout in milliseconds. - + spring.redis.password= # Login password of the redis server. + spring.redis.port=6379 # Redis server port. + spring.redis.sentinel.master= # Name of Redis server. + spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs. + spring.redis.ssl=false # Enable SSL support. + spring.redis.timeout=0 # Connection timeout in milliseconds. # TRANSACTION ({sc-spring-boot-autoconfigure}/transaction/TransactionProperties.{sc-ext}[TransactionProperties]) spring.transaction.default-timeout= # Default transaction timeout in seconds. spring.transaction.rollback-on-commit-failure= # Perform the rollback on commit failures. + + # ---------------------------------------- # INTEGRATION PROPERTIES # ---------------------------------------- diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index e05e2b95cbe..46b8627de39 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -3075,12 +3075,14 @@ In this example we are using a single application context (the one created by th listener) and attaching it to the `DispatcherServlet` using an init parameter. This is normal in a Spring Boot application (you normally only have one application context). + + [[howto-use-lettuce-instead-of-jedis]] === Use Lettuce instead of Jedis -The Spring Boot Redis starter (`spring-boot-starter-data-redis` in particular) uses +The Spring Boot starter (`spring-boot-starter-data-redis`) uses https://github.com/xetorthio/jedis/[Jedis] by default. You need to exclude that dependency -and include the https://github.com/lettuce-io/lettuce-core/[Lettuce] one instead. Spring Boot provides a managed dependency -to help make this process as easy as possible. +and include the https://github.com/lettuce-io/lettuce-core/[Lettuce] one instead. Spring +Boot manages that dependency to help make this process as easy as possible. Example in Maven: @@ -3114,4 +3116,4 @@ Example in Gradle: compile("io.lettuce:lettuce-core:{lettuce.version}") // ... } ----- \ No newline at end of file +----