diff --git a/redis/reactive/pom.xml b/redis/reactive/pom.xml
index 98637fc4..2b89bca7 100644
--- a/redis/reactive/pom.xml
+++ b/redis/reactive/pom.xml
@@ -29,6 +29,12 @@
lettuce-core
+
+ io.projectreactor
+ reactor-test
+ test
+
+
${project.groupId}
spring-data-redis-example-utils
diff --git a/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java b/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java
index 26c7d1dc..41edf738 100644
--- a/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java
+++ b/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 the original author or authors.
+ * Copyright 2016-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.
@@ -20,8 +20,11 @@ import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.core.ReactiveRedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
/**
* @author Mark Paluch
@@ -32,10 +35,15 @@ public class RedisTestConfiguration {
@Autowired RedisConnectionFactory factory;
@Bean
- public RedisConnectionFactory redisConnectionFactory() {
+ public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
+ @Bean
+ public ReactiveRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
+ return new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext.string());
+ }
+
/**
* Clear database before shut down.
*/
diff --git a/redis/reactive/src/test/java/example/springdata/redis/operations/ListOperationsTests.java b/redis/reactive/src/test/java/example/springdata/redis/operations/ListOperationsTests.java
new file mode 100644
index 00000000..5390c9a4
--- /dev/null
+++ b/redis/reactive/src/test/java/example/springdata/redis/operations/ListOperationsTests.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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 example.springdata.redis.operations;
+
+import example.springdata.redis.RedisTestConfiguration;
+import example.springdata.redis.test.util.RequiresRedisServer;
+import lombok.extern.slf4j.Slf4j;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.time.Duration;
+import java.util.logging.Level;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+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.data.redis.core.ReactiveListOperations;
+import org.springframework.data.redis.core.ReactiveRedisOperations;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * Show usage of reactive Template API on Redis lists using {@link ReactiveRedisOperations}.
+ *
+ * @author Mark Paluch
+ */
+@Slf4j
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = RedisTestConfiguration.class)
+public class ListOperationsTests {
+
+ // we only want to run this tests when redis is up an running
+ public static @ClassRule RequiresRedisServer requiresServer = RequiresRedisServer.onLocalhost();
+
+ @Autowired ReactiveRedisOperations operations;
+
+ @Before
+ public void before() {
+ StepVerifier.create(operations.execute(it -> it.serverCommands().flushDb())).expectNext("OK").verifyComplete();
+ }
+
+ /**
+ * A simple queue using Redis blocking list commands {@code BLPOP} and {@code LPUSH} to produce the queue message.
+ */
+ @Test
+ public void shouldPollAndPopulateQueue() {
+
+ String queue = "foo";
+
+ ReactiveListOperations listOperations = operations.opsForList();
+
+ Mono blpop = listOperations //
+ .leftPop(queue, Duration.ofSeconds(30)) //
+ .log("example.springdata.redis", Level.INFO);
+
+ log.info("Blocking pop...waiting for message");
+ StepVerifier.create(blpop) //
+ .then(() -> {
+
+ Mono.delay(Duration.ofSeconds(10)).doOnSuccess(it -> {
+
+ log.info("Subscriber produces message");
+
+ }).then(listOperations.leftPush(queue, "Hello, World!")).subscribe();
+
+ }).expectNext("Hello, World!").verifyComplete();
+
+ log.info("Blocking pop...done!");
+ }
+}
diff --git a/redis/reactive/src/test/java/example/springdata/redis/operations/ValueOperationsTests.java b/redis/reactive/src/test/java/example/springdata/redis/operations/ValueOperationsTests.java
new file mode 100644
index 00000000..2492417b
--- /dev/null
+++ b/redis/reactive/src/test/java/example/springdata/redis/operations/ValueOperationsTests.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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 example.springdata.redis.operations;
+
+import static org.assertj.core.api.Assertions.*;
+
+import example.springdata.redis.RedisTestConfiguration;
+import example.springdata.redis.test.util.RequiresRedisServer;
+import lombok.extern.slf4j.Slf4j;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.time.Duration;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+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.data.redis.core.ReactiveRedisOperations;
+import org.springframework.data.redis.core.ReactiveValueOperations;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * Show usage of reactive Template API on Redis strings using {@link ReactiveRedisOperations}.
+ *
+ * @author Mark Paluch
+ */
+@Slf4j
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = RedisTestConfiguration.class)
+public class ValueOperationsTests {
+
+ // we only want to run this tests when redis is up an running
+ public static @ClassRule RequiresRedisServer requiresServer = RequiresRedisServer.onLocalhost();
+
+ @Autowired ReactiveRedisOperations operations;
+
+ @Before
+ public void before() {
+ StepVerifier.create(operations.execute(it -> it.serverCommands().flushDb())).expectNext("OK").verifyComplete();
+ }
+
+ /**
+ * Implement a simple caching sequence using {@code GET} and {@code SETEX} commands.
+ */
+ @Test
+ public void shouldCacheValue() {
+
+ String cacheKey = "foo";
+
+ ReactiveValueOperations valueOperations = operations.opsForValue();
+
+ Mono cachedMono = valueOperations.get(cacheKey) //
+ .switchIfEmpty(cacheValue().flatMap(it -> {
+
+ return valueOperations.set(cacheKey, it, Duration.ofSeconds(60)).then(Mono.just(it));
+ }));
+
+ log.info("Initial access (takes a while...)");
+
+ StepVerifier.create(cachedMono).expectSubscription() //
+ .expectNoEvent(Duration.ofSeconds(9)) //
+ .expectNext("Hello, World!") //
+ .verifyComplete();
+
+ log.info("Subsequent access (use cached value)");
+
+ Duration duration = StepVerifier.create(cachedMono) //
+ .expectNext("Hello, World!") //
+ .verifyComplete();
+
+ log.info("Done");
+
+ assertThat(duration).isLessThan(Duration.ofSeconds(2));
+ }
+
+ /**
+ * @return the cache value that is expensive to calculate.
+ */
+ private Mono cacheValue() {
+ return Mono.delay(Duration.ofSeconds(10)).then(Mono.just("Hello, World!"));
+ }
+}
diff --git a/redis/reactive/src/test/resources/application.properties b/redis/reactive/src/test/resources/application.properties
index bac6605c..c96b3847 100644
--- a/redis/reactive/src/test/resources/application.properties
+++ b/redis/reactive/src/test/resources/application.properties
@@ -1 +1,2 @@
logging.level.root=WARN
+logging.level.example.springdata.redis=INFO