diff --git a/pom.xml b/pom.xml index 66c365fe7..7adc8d870 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,7 @@ redisson-all redisson-tomcat redisson-spring-data + redisson-spring-boot-starter diff --git a/redisson-spring-boot-starter/README.md b/redisson-spring-boot-starter/README.md new file mode 100644 index 000000000..13f4d95ae --- /dev/null +++ b/redisson-spring-boot-starter/README.md @@ -0,0 +1,46 @@ +Spring Boot Starter +=== + +Integrates Redisson with Spring Boot library. + +Supports Spring Data Redis 1.3.x, 1.4.x, 1.5.x, 2.0.x + +Usage +=== + +### 1. Add `redisson-spring-boot-starter` dependency into your project: + +1. __For JDK 1.8+__ + + Maven + ```xml + + org.redisson + redisson-spring-boot-starter + 3.8.0 + + ``` + Gradle + + ```java + compile 'org.redisson:redisson-spring-boot-starter:3.8.0' + ``` + +2. __For JDK 1.6+__ + + Maven + ```xml + + org.redisson + redisson-spring-boot-starter + 2.13.0 + + ``` + Gradle + + ```java + compile 'org.redisson:redisson-spring-boot-starter:2.13.0' + ``` + + +### 2. Get access to Redisson through spring bean with `RedissonClient` interface diff --git a/redisson-spring-boot-starter/pom.xml b/redisson-spring-boot-starter/pom.xml new file mode 100644 index 000000000..8d453ff4b --- /dev/null +++ b/redisson-spring-boot-starter/pom.xml @@ -0,0 +1,129 @@ + + 4.0.0 + + + org.redisson + redisson-parent + 2.12.6-SNAPSHOT + ../ + + + + 1.5.15.RELEASE + + + redisson-spring-boot-starter + jar + + Redisson/Spring Boot Starter + + + + + maven-compiler-plugin + + + + maven-javadoc-plugin + + + com.mycila + license-maven-plugin + 3.0 + + ${basedir} +
${basedir}/../header.txt
+ false + true + false + + src/main/java/org/redisson/ + + + target/** + + true + + JAVADOC_STYLE + + true + true + UTF-8 +
+ + + + check + + + +
+
+
+ + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.redisson + redisson + ${project.version} + + + + org.redisson + redisson-spring-data-18 + ${project.version} + + + + org.assertj + assertj-core + 3.10.0 + test + + + + junit + junit + 4.12 + test + + + + org.springframework.boot + spring-boot-starter-test + test + + + +
diff --git a/redisson-spring-boot-starter/src/main/java/org/redisson/spring/starter/RedissonAutoConfiguration.java b/redisson-spring-boot-starter/src/main/java/org/redisson/spring/starter/RedissonAutoConfiguration.java new file mode 100644 index 000000000..51d924353 --- /dev/null +++ b/redisson-spring-boot-starter/src/main/java/org/redisson/spring/starter/RedissonAutoConfiguration.java @@ -0,0 +1,149 @@ +/** + * Copyright 2018 Nikita Koksharov + * + * 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.redisson.spring.starter; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.List; + +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.util.ReflectionUtils; + +/** + * + * @author Nikita Koksharov + * + */ +@Configuration +@ConditionalOnClass(Redisson.class) +@AutoConfigureAfter(RedisAutoConfiguration.class) +@EnableConfigurationProperties(RedissonProperties.class) +public class RedissonAutoConfiguration { + + @Autowired + private RedissonProperties redissonProperties; + + @Autowired + private RedisProperties redisProperties; + + @Autowired + private ApplicationContext ctx; + + @Bean + @ConditionalOnMissingBean(RedisConnectionFactory.class) + public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) { + return new RedissonConnectionFactory(redisson); + } + + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(RedissonClient.class) + public RedissonClient redisson() throws IOException { + Config config = null; + Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster"); + Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout"); + Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, redisProperties); + int timeout; + if (!(timeoutValue instanceof Integer)) { + Method millisMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis"); + timeout = ((Long) ReflectionUtils.invokeMethod(millisMethod, timeoutValue)).intValue(); + } else { + timeout = (Integer)timeoutValue; + } + + if (redissonProperties.getConfig() != null) { + try { + InputStream is = getConfigStream(); + config = Config.fromJSON(is); + } catch (IOException e) { + // trying next format + try { + InputStream is = getConfigStream(); + config = Config.fromYAML(is); + } catch (IOException e1) { + throw new IllegalArgumentException("Can't parse config", e1); + } + } + } else if (redisProperties.getSentinel() != null) { + Method nodesMethod = ReflectionUtils.findMethod(Sentinel.class, "getNodes"); + Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, redisProperties.getSentinel()); + + String[] nodes; + if (nodesValue instanceof String) { + nodes = ((String)nodesValue).split(","); + } else { + nodes = ((List)nodesValue).toArray(new String[((List)nodesValue).size()]); + } + + config = new Config(); + config.useSentinelServers() + .setMasterName(redisProperties.getSentinel().getMaster()) + .addSentinelAddress(nodes) + .setDatabase(redisProperties.getDatabase()) + .setConnectTimeout(timeout) + .setPassword(redisProperties.getPassword()); + } else if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, redisProperties) != null) { + Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, redisProperties); + Method nodesMethod = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes"); + List nodesObject = (List) ReflectionUtils.invokeMethod(nodesMethod, clusterObject); + + config = new Config(); + config.useClusterServers() + .addNodeAddress(nodesObject.toArray(new String[nodesObject.size()])) + .setConnectTimeout(timeout) + .setPassword(redisProperties.getPassword()); + } else { + config = new Config(); + String prefix = "redis://"; + Method method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl"); + if (method != null && (Boolean)ReflectionUtils.invokeMethod(method, redisProperties)) { + prefix = "rediss://"; + } + + config.useSingleServer() + .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort()) + .setConnectTimeout(timeout) + .setDatabase(redisProperties.getDatabase()) + .setPassword(redisProperties.getPassword()); + } + + return Redisson.create(config); + } + + protected InputStream getConfigStream() throws IOException { + Resource resource = ctx.getResource(redissonProperties.getConfig()); + InputStream is = resource.getInputStream(); + return is; + } + + +} diff --git a/redisson-spring-boot-starter/src/main/java/org/redisson/spring/starter/RedissonProperties.java b/redisson-spring-boot-starter/src/main/java/org/redisson/spring/starter/RedissonProperties.java new file mode 100644 index 000000000..e4c35da97 --- /dev/null +++ b/redisson-spring-boot-starter/src/main/java/org/redisson/spring/starter/RedissonProperties.java @@ -0,0 +1,38 @@ +/** + * Copyright 2018 Nikita Koksharov + * + * 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.redisson.spring.starter; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * + * @author Nikita Koksharov + * + */ +@ConfigurationProperties(prefix = "spring.redis.redisson") +public class RedissonProperties { + + private String config; + + public String getConfig() { + return config; + } + + public void setConfig(String config) { + this.config = config; + } + +} diff --git a/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonApplication.java b/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonApplication.java new file mode 100644 index 000000000..cb844f490 --- /dev/null +++ b/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonApplication.java @@ -0,0 +1,15 @@ +package org.redisson.spring.starter; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; + +@SpringBootApplication +@EnableCaching +public class RedissonApplication { + + public static void main(String[] args) { + SpringApplication.run(RedissonApplication.class, args); + } + +} diff --git a/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonAutoConfigurationTest.java b/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonAutoConfigurationTest.java new file mode 100644 index 000000000..4c00f38b4 --- /dev/null +++ b/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonAutoConfigurationTest.java @@ -0,0 +1,43 @@ +package org.redisson.spring.starter; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.redisson.api.RMap; +import org.redisson.api.RedissonClient; +import org.redisson.client.codec.StringCodec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.BoundHashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = RedissonApplication.class, + properties = { + "spring.redis.redisson.config=classpath:redisson.yaml", + "spring.redis.timeout=10000" + }) +public class RedissonAutoConfigurationTest { + + @Autowired + private RedissonClient redisson; + + @Autowired + private RedisTemplate template; + + @Test + public void testApp() { + redisson.getKeys().flushall(); + + RMap m = redisson.getMap("test", StringCodec.INSTANCE); + m.put("1", "2"); + + BoundHashOperations hash = template.boundHashOps("test"); + String t = hash.get("1"); + assertThat(t).isEqualTo("2"); + } + +} diff --git a/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonCacheManagerAutoConfigurationTest.java b/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonCacheManagerAutoConfigurationTest.java new file mode 100644 index 000000000..f30cb8bc1 --- /dev/null +++ b/redisson-spring-boot-starter/src/test/java/org/redisson/spring/starter/RedissonCacheManagerAutoConfigurationTest.java @@ -0,0 +1,31 @@ +package org.redisson.spring.starter; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = RedissonApplication.class, + properties = { + "spring.redis.redisson.config=classpath:redisson.yaml", + "spring.redis.timeout=10000", + "spring.cache.type=redis", + }) +public class RedissonCacheManagerAutoConfigurationTest { + + @Autowired + private CacheManager cacheManager; + + @Test + public void testApp() { + Cache cache = cacheManager.getCache("test"); + Assert.assertNotNull(cache); + } + +} diff --git a/redisson-spring-boot-starter/src/test/resources/redisson.yaml b/redisson-spring-boot-starter/src/test/resources/redisson.yaml new file mode 100644 index 000000000..28466f3ba --- /dev/null +++ b/redisson-spring-boot-starter/src/test/resources/redisson.yaml @@ -0,0 +1,2 @@ +singleServerConfig: + address: "redis://127.0.0.1:6379" \ No newline at end of file