diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java index ade1968ffe..8310ab9ba7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration.java @@ -18,17 +18,23 @@ package org.springframework.boot.autoconfigure.redis; import java.net.UnknownHostException; +import org.apache.commons.pool.impl.GenericObjectPool; import org.springframework.beans.factory.annotation.Autowired; 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.condition.ConditionalOnMissingClass; +import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration.RedisProperties.Pool; import org.springframework.boot.context.properties.ConfigurationProperties; 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.PoolConfig; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.DefaultLettucePool; import org.springframework.data.redis.connection.lettuce.LettuceConnection; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePool; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; @@ -43,14 +49,14 @@ import org.springframework.data.redis.core.StringRedisTemplate; public class RedisAutoConfiguration { @Configuration - @EnableConfigurationProperties(RedisProperties.class) - protected static class RedisConfiguration { + @ConditionalOnMissingClass(name = "org.apache.commons.pool.impl.GenericObjectPool") + protected static class RedisConnectionConfiguration { @Autowired private RedisProperties config; @Bean - @ConditionalOnMissingBean(RedisConnectionFactory.class) + @ConditionalOnMissingBean RedisConnectionFactory redisConnectionFactory() throws UnknownHostException { LettuceConnectionFactory factory = new LettuceConnectionFactory( this.config.getHost(), this.config.getPort()); @@ -60,6 +66,58 @@ public class RedisAutoConfiguration { return factory; } + } + + @Configuration + @ConditionalOnClass(GenericObjectPool.class) + protected static class RedisPooledConnectionConfiguration { + + @Autowired + private RedisProperties config; + + @Bean + @ConditionalOnMissingBean + RedisConnectionFactory redisConnectionFactory() throws UnknownHostException { + if (this.config.getPool() != null) { + LettuceConnectionFactory factory = new LettuceConnectionFactory( + lettucePool()); + return factory; + } + LettuceConnectionFactory factory = new LettuceConnectionFactory( + this.config.getHost(), this.config.getPort()); + if (this.config.getPassword() != null) { + factory.setPassword(this.config.getPassword()); + } + return factory; + } + + @Bean + @ConditionalOnMissingBean + public LettucePool lettucePool() { + return new DefaultLettucePool(this.config.getHost(), this.config.getPort(), + poolConfig()); + } + + private PoolConfig poolConfig() { + PoolConfig pool = new PoolConfig(); + Pool props = this.config.getPool(); + if (props != null) { + pool.setMaxActive(props.getMaxActive()); + pool.setMaxIdle(props.getMaxIdle()); + pool.setMinIdle(props.getMinIdle()); + pool.setMaxWait(props.getMaxWait()); + } + return pool; + } + } + + @Configuration + @EnableConfigurationProperties(RedisProperties.class) + protected static class RedisConfiguration { + + @Autowired + private RedisProperties config; + @Bean @ConditionalOnMissingBean(name = "redisTemplate") RedisOperations redisTemplate( @@ -91,6 +149,8 @@ public class RedisAutoConfiguration { private int port = 6379; + private Pool pool; + public String getHost() { return this.host; } @@ -115,6 +175,53 @@ public class RedisAutoConfiguration { this.password = password; } + public Pool getPool() { + return this.pool; + } + + public void setPool(Pool pool) { + this.pool = pool; + } + + public static class Pool { + private int maxIdle = 8; + private int minIdle = 0; + private int maxActive = 8; + private int maxWait = -1; + + public int getMaxIdle() { + return this.maxIdle; + } + + public void setMaxIdle(int maxIdle) { + this.maxIdle = maxIdle; + } + + public int getMinIdle() { + return this.minIdle; + } + + public void setMinIdle(int minIdle) { + this.minIdle = minIdle; + } + + public int getMaxActive() { + return this.maxActive; + } + + public void setMaxActive(int maxActive) { + this.maxActive = maxActive; + } + + public int getMaxWait() { + return this.maxWait; + } + + public void setMaxWait(int maxWait) { + this.maxWait = maxWait; + } + } + } } diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 0ee8f5348c..c16cec7ccb 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -23,6 +23,7 @@ spring-boot-sample-batch spring-boot-sample-data-jpa spring-boot-sample-data-mongodb + spring-boot-sample-data-redis spring-boot-sample-integration spring-boot-sample-jetty spring-boot-sample-profile diff --git a/spring-boot-samples/spring-boot-sample-data-redis/pom.xml b/spring-boot-samples/spring-boot-sample-data-redis/pom.xml new file mode 100644 index 0000000000..c2a654deaf --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-redis/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 0.5.0.BUILD-SNAPSHOT + + spring-boot-sample-data-redis + jar + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.data + spring-data-redis + + + com.lambdaworks + lettuce + + + + + production + + + + commons-pool + commons-pool + pom.lastUpdated + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-data-redis/src/main/java/org/springframework/boot/sample/data/redis/SampleRedisApplication.java b/spring-boot-samples/spring-boot-sample-data-redis/src/main/java/org/springframework/boot/sample/data/redis/SampleRedisApplication.java new file mode 100644 index 0000000000..8e757e68c9 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-redis/src/main/java/org/springframework/boot/sample/data/redis/SampleRedisApplication.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2013 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.sample.data.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; + +@Configuration +@EnableAutoConfiguration +public class SampleRedisApplication implements CommandLineRunner { + + @Autowired + private StringRedisTemplate template; + + @Override + public void run(String... args) throws Exception { + ValueOperations ops = template.opsForValue(); + String key = "spring.boot.redis.test"; + if (!template.hasKey(key)) { + ops.set(key, "foo"); + } + System.out.println("Found key " + key + ", value=" + ops.get(key)); + } + + public static void main(String[] args) throws Exception { + // Close the context so it doesn't stay awake listening for redis + SpringApplication.run(SampleRedisApplication.class, args).close(); + } +} diff --git a/spring-boot-samples/spring-boot-sample-data-redis/src/test/java/org/springframework/boot/sample/data/redis/SampleRedisApplicationTests.java b/spring-boot-samples/spring-boot-sample-data-redis/src/test/java/org/springframework/boot/sample/data/redis/SampleRedisApplicationTests.java new file mode 100644 index 0000000000..b72aa170d7 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-redis/src/test/java/org/springframework/boot/sample/data/redis/SampleRedisApplicationTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2013 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.sample.data.redis; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.boot.OutputCapture; +import org.springframework.boot.sample.data.redis.SampleRedisApplication; +import org.springframework.core.NestedCheckedException; + +/** + * Tests for {@link SampleRedisApplication}. + * + * @author Dave Syer + */ +public class SampleRedisApplicationTests { + + @Rule + public OutputCapture outputCapture = new OutputCapture(); + + @Test + public void testDefaultSettings() throws Exception { + try { + SampleRedisApplication.main(new String[0]); + } + catch (IllegalStateException ex) { + if (serverNotRunning(ex)) { + return; + } + } + String output = this.outputCapture.toString(); + assertTrue("Wrong output: " + output, + output.contains("Found key spring.boot.redis.test")); + } + + private boolean serverNotRunning(IllegalStateException e) { + @SuppressWarnings("serial") + NestedCheckedException nested = new NestedCheckedException("failed", e) { + }; + if (nested.contains(IOException.class)) { + Throwable root = nested.getRootCause(); + if (root.getMessage().contains("couldn't connect to [localhost")) { + return true; + } + } + return false; + } + +}