diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..e440161
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,32 @@
+version: 2
+updates:
+
+ - package-ecosystem: "maven"
+ directories:
+ - "/initial"
+ - "/complete"
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-patch"]
+ schedule:
+ interval: "monthly"
+ target-branch: "main"
+ groups:
+ guide-dependencies-maven:
+ patterns:
+ - "*"
+
+ - package-ecosystem: "gradle"
+ directories:
+ - "/initial"
+ - "/complete"
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-patch"]
+ schedule:
+ interval: "monthly"
+ target-branch: "main"
+ groups:
+ guide-dependencies-gradle:
+ patterns:
+ - "*"
\ No newline at end of file
diff --git a/.github/workflows/continuous-integration-build.yml b/.github/workflows/continuous-integration-build.yml
new file mode 100644
index 0000000..5d3e68e
--- /dev/null
+++ b/.github/workflows/continuous-integration-build.yml
@@ -0,0 +1,13 @@
+name: CI Build
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ build:
+ uses: spring-guides/getting-started-macros/.github/workflows/build_initial_complete_maven_gradle.yml@main
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
deleted file mode 100644
index 2087732..0000000
--- a/Jenkinsfile
+++ /dev/null
@@ -1,44 +0,0 @@
-pipeline {
- agent none
-
- triggers {
- pollSCM 'H/10 * * * *'
- }
-
- options {
- disableConcurrentBuilds()
- buildDiscarder(logRotator(numToKeepStr: '14'))
- }
-
- stages {
- stage("test: baseline (jdk17)") {
- agent {
- docker {
- image 'harbor-repo.vmware.com/dockerhub-proxy-cache/library/adoptopenjdk/openjdk17:latest'
- args '-v $HOME/.m2:/tmp/jenkins-home/.m2'
- }
- }
- options { timeout(time: 30, unit: 'MINUTES') }
- steps {
- sh 'test/run.sh'
- }
- }
-
- }
-
- post {
- changed {
- script {
- slackSend(
- color: (currentBuild.currentResult == 'SUCCESS') ? 'good' : 'danger',
- channel: '#sagan-content',
- message: "${currentBuild.fullDisplayName} - `${currentBuild.currentResult}`\n${env.BUILD_URL}")
- emailext(
- subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}",
- mimeType: 'text/html',
- recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']],
- body: "${currentBuild.fullDisplayName} is reported as ${currentBuild.currentResult}")
- }
- }
- }
-}
diff --git a/README.adoc b/README.adoc
index b49c674..53638cf 100644
--- a/README.adoc
+++ b/README.adoc
@@ -2,98 +2,55 @@
:icons: font
:source-highlighter: prettify
:project_id: gs-spring-data-reactive-redis
+:java_version: 17
+:build_system: maven
+:build_name: demo
+:build_version: 0.0.1-SNAPSHOT
+:network_container: guide-redis
+
This guide walks you through the process of creating a functional reactive application that uses Spring Data to interact with Redis using the non-blocking Lettuce driver.
== What You Will Build
You'll build a Spring application that uses https://projects.spring.io/spring-data-redis/[Spring Data Redis] and https://projectreactor.io/[Project Reactor] to interact with a Redis data store reactively, storing and retrieving `Coffee` objects without blocking. This application uses Reactor's `Publisher` implementations based upon the Reactive Streams specification, namely `Mono` (for a Publisher returning 0 or 1 value) and `Flux` (for a Publisher returning 0 to n values).
-== What You Need
+include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/guide_introduction.adoc[]
-:java_version: 17
-include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/prereq_editor_jdk_buildtools.adoc[]
+== Setting up the Redis Server
+
+Before you can build a messaging application, you need to set up the server to
+handle receiving and sending messages.
+include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/docker_compose_support.adoc[]
-include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/how_to_complete_this_guide.adoc[]
+If you choose to run the Redis server yourself instead of using Spring Boot Docker Compose support, you have a few options:
+- https://redis.io/download[Download the server] and manually run it
+- Install with Homebrew, if you use a Mac
+- Manually run the `compose.yaml` file with `docker compose up`
+
+If you go with any of these alternate approaches, you should remove the `spring-boot-docker-compose` dependency from the Maven or Gradle build file.
+You also need to add configuration to an `application.properties` file, as described in greater detail in the <<_preparing_to_build_the_application>> section.
+As mentioned earlier, this guide assumes that you use Docker Compose support in Spring Boot, so additional changes to `application.properties` are not required at this point.
[[scratch]]
== Starting with Spring Initializr
-You can use this https://start.spring.io/#!type=maven-project&language=java&packaging=jar&jvmVersion=17&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=data-redis-reactive,lombok,webflux[pre-initialized project] and click Generate to download a ZIP file. This project is configured to fit the examples in this tutorial.
+You can use this https://start.spring.io/#!type=maven-project&language=java&packaging=jar&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=data-redis-reactive,webflux,docker-compose[pre-initialized project] and click Generate to download a ZIP file. This project is configured to fit the examples in this tutorial.
To manually initialize the project:
. Navigate to https://start.spring.io.
This service pulls in all the dependencies you need for an application and does most of the setup for you.
. Choose either Gradle or Maven and the language you want to use. This guide assumes that you chose Java.
-. Click *Dependencies* and select *Spring Reactive Web*, *Spring Data Reactive Redis*, and *Lombok*.
+. Click *Dependencies* and select *Spring Reactive Web*, *Spring Data Reactive Redis*, and *Docker Compose Support*.
. Click *Generate*.
. Download the resulting ZIP file, which is an archive of a web application that is configured with your choices.
NOTE: If your IDE has the Spring Initializr integration, you can complete this process from your IDE.
-NOTE: You can also fork the project from Github and open it in your IDE or other editor.
-
-[[scratch]]
-== Standing up a Redis Server
-
-Before you can build a messaging application, you need to set up the server that will
-handle receiving and sending messages.
-
-Redis is an open source, BSD-licensed, key-value data store that also comes with a
-messaging system. The server is freely available at https://redis.io/download. You can
-download it manually, or, if you use a Mac, with Homebrew, by running the following
-command in a terminal window:
-
-====
-[source,bash]
-----
-brew install redis
-----
-====
-
-Once you unpack Redis, you can launch it with its default settings by running the following command:
-
-====
-[source,bash]
-----
-redis-server
-----
-====
-
-You should see a message similar to the following:
-
-====
-[source,text]
-----
-[35142] 01 May 14:36:28.939 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
-[35142] 01 May 14:36:28.940 * Max number of open files set to 10032
- _._
- _.-``__ ''-._
- _.-`` `. `_. ''-._ Redis 2.6.12 (00000000/0) 64 bit
- .-`` .-```. ```\/ _.,_ ''-._
- ( ' , .-` | `, ) Running in stand alone mode
- |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
- | `-._ `._ / _.-' | PID: 35142
- `-._ `-._ `-./ _.-' _.-'
- |`-._`-._ `-.__.-' _.-'_.-'|
- | `-._`-._ _.-'_.-' | https://redis.io
- `-._ `-._`-.__.-'_.-' _.-'
- |`-._`-._ `-.__.-' _.-'_.-'|
- | `-._`-._ _.-'_.-' |
- `-._ `-._`-.__.-'_.-' _.-'
- `-._ `-.__.-' _.-'
- `-._ _.-'
- `-.__.-'
-
-[35142] 01 May 14:36:28.941 # Server started, Redis version 2.6.12
-[35142] 01 May 14:36:28.941 * The server is now ready to accept connections on port 6379
-----
-====
-
[[initial]]
== Create a Domain Class
-Create a class representing a type of coffee we wish to stock in our coffee catalog:
+Create a record representing a type of coffee we wish to stock in our coffee catalog:
`src/main/java/com/example/demo/Coffee.java`
[source,java,tabsize=2]
@@ -101,9 +58,6 @@ Create a class representing a type of coffee we wish to stock in our coffee cata
include::complete/src/main/java/com/example/demo/Coffee.java[]
----
-NOTE: I use Lombok in this example to eliminate the boilerplate code for constructors and so-called "data class" methods ( accessors/mutators, `equals()`, `toString()`, & `hashCode()`).
-
-
== Create a Configuration Class
Create a class that includes Spring Beans that support reactive Redis operations:
@@ -114,7 +68,6 @@ Create a class that includes Spring Beans that support reactive Redis operations
include::complete/src/main/java/com/example/demo/CoffeeConfiguration.java[]
----
-
== Create a Spring Bean to Load Data
Create a Spring Bean to load sample data for our application when we start it:
@@ -138,29 +91,83 @@ Create a `RestController` to provide an external interface for our application:
include::complete/src/main/java/com/example/demo/CoffeeController.java[]
----
+== Run the Application
-== Make the Application Executable
+You can run the main method through your IDE.
+Note that, if you have cloned the project from the solution repository, your IDE may look in the wrong place for the `compose.yaml` file.
+You can configure your IDE to look in the correct place or you could use the command line to run the application.
+The `./gradlew bootRun` and `./mvnw spring-boot:run` commands will launch the application and automatically find the compose.yaml file.
-Although you can package this service as a traditional link:/understanding/WAR[WAR] file for deployment to an external application server, the simpler approach shown here creates a standalone application. You package everything in a single, executable JAR file, driven by a good old Java `main()` method. Along the way, you use Spring's support for embedding the link:/understanding/Netty[Netty] an asynchronous "container" as the HTTP runtime instead of deploying to an external instance.
+== Test the Application
+With the application running, run the following command from a new terminal:
+[source,bash]
+```
+curl http://localhost:8080/coffees
+```
-`src/main/java/com/example/demo/DemoApplication.java`
-[source,java,tabsize=2]
+You should see the following output:
+[source,json]
+```
+[
+ {
+ "id": "04ce0843-c9f8-40f6-942f-1ff643c1d426",
+ "name": "Jet Black Redis"
+ },
+ {
+ "id": "e2a0d798-5fa4-48a2-a45c-7770d8bb82bf",
+ "name": "Black Alert Redis"
+ },
+ {
+ "id": "13f13e3a-0798-44b7-8ae4-b319b227bb19",
+ "name": "Darth Redis"
+ }
+]
+```
+
+== Preparing to Build the Application
+
+To run the code without Spring Boot Docker Compose support, you need a version of Redis running locally to connect to.
+To do this, you can use Docker Compose, but you must first make two changes to the `compose.yaml` file.
+First, modify the `ports` entry in `compose.yaml` to be `'6379:6379'`.
+Second, add a `container_name`.
+
+The `compose.yaml` should now be:
----
-include::complete/src/main/java/com/example/demo/DemoApplication.java[]
+services:
+ redis:
+ container_name: 'guide-redis'
+ image: 'redis:latest'
+ ports:
+ - '6379:6379'
----
-include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/spring-boot-application.adoc[]
+You can now run `docker compose up` to start the Redis server.
+Now you should have an external Redis server that is ready to accept requests.
+You can rerun the application and see the same output using your external Redis server.
-include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/build_an_executable_jar_subhead.adoc[]
+NOTE: No configuration is required in the `application.properties` file because the default values match the Redis server configuration in `compose.yaml`. Specifically, the properties `spring.data.redis.host` and `spring.data.redis.port` default to `localhost` and `6379` respectively.
-include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/build_an_executable_jar_with_both.adoc[]
+include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/build_and_execute_guide.adoc[]
+== Test the Application in Docker
-== Test the application
+If you ran the application using a Docker instruction (shown earlier), a simple curl command from a terminal or command line will no longer work.
+This is because we run our containers in a https://docs.docker.com/compose/networking/[Docker network] that is not accessible from the terminal or command line. To run curl commands, we can start a third container to run our curl commands and attach it to the same network.
-Now that the application is running, you can test it by accessing `http://localhost:8080/coffees` from HTTPie, curl, or your favorite browser.
+First, obtain an interactive shell to a new container that runs on the same network as the Redis container and the application:
+[source, bash]
+----
+docker run --rm --network container:guide-redis -it alpine
+----
+
+Next, from the shell inside the container, install curl:
+[source, bash]
+----
+apk add curl
+----
+Finally, you can run the curl commands as described in <<_test_the_application>>.
== Summary
diff --git a/complete/build.gradle b/complete/build.gradle
index 3fb0c12..ab14919 100644
--- a/complete/build.gradle
+++ b/complete/build.gradle
@@ -21,8 +21,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
- compileOnly 'org.projectlombok:lombok'
- annotationProcessor 'org.projectlombok:lombok'
+ developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
diff --git a/complete/compose.yaml b/complete/compose.yaml
new file mode 100644
index 0000000..f310cd2
--- /dev/null
+++ b/complete/compose.yaml
@@ -0,0 +1,5 @@
+services:
+ redis:
+ image: 'redis:latest'
+ ports:
+ - '6379'
\ No newline at end of file
diff --git a/complete/pom.xml b/complete/pom.xml
index bc2a7e0..8eedd3a 100644
--- a/complete/pom.xml
+++ b/complete/pom.xml
@@ -25,10 +25,10 @@
org.springframework.boot
spring-boot-starter-webflux
-
- org.projectlombok
- lombok
+ org.springframework.boot
+ spring-boot-docker-compose
+ runtime
true
@@ -45,18 +45,7 @@
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
+
diff --git a/complete/src/main/java/com/example/demo/Coffee.java b/complete/src/main/java/com/example/demo/Coffee.java
index fedf616..ea8d020 100644
--- a/complete/src/main/java/com/example/demo/Coffee.java
+++ b/complete/src/main/java/com/example/demo/Coffee.java
@@ -1,13 +1,4 @@
package com.example.demo;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
-public class Coffee {
- private String id;
- private String name;
+public record Coffee(String id, String name) {
}
\ No newline at end of file
diff --git a/complete/src/main/java/com/example/demo/CoffeeLoader.java b/complete/src/main/java/com/example/demo/CoffeeLoader.java
index ace4c1a..cea3e38 100644
--- a/complete/src/main/java/com/example/demo/CoffeeLoader.java
+++ b/complete/src/main/java/com/example/demo/CoffeeLoader.java
@@ -23,7 +23,7 @@ public void loadData() {
factory.getReactiveConnection().serverCommands().flushAll().thenMany(
Flux.just("Jet Black Redis", "Darth Redis", "Black Alert Redis")
.map(name -> new Coffee(UUID.randomUUID().toString(), name))
- .flatMap(coffee -> coffeeOps.opsForValue().set(coffee.getId(), coffee)))
+ .flatMap(coffee -> coffeeOps.opsForValue().set(coffee.id(), coffee)))
.thenMany(coffeeOps.keys("*")
.flatMap(coffeeOps.opsForValue()::get))
.subscribe(System.out::println);
diff --git a/initial/build.gradle b/initial/build.gradle
index 3fb0c12..ab14919 100644
--- a/initial/build.gradle
+++ b/initial/build.gradle
@@ -21,8 +21,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
- compileOnly 'org.projectlombok:lombok'
- annotationProcessor 'org.projectlombok:lombok'
+ developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
diff --git a/initial/compose.yaml b/initial/compose.yaml
new file mode 100644
index 0000000..f310cd2
--- /dev/null
+++ b/initial/compose.yaml
@@ -0,0 +1,5 @@
+services:
+ redis:
+ image: 'redis:latest'
+ ports:
+ - '6379'
\ No newline at end of file
diff --git a/initial/pom.xml b/initial/pom.xml
index bc2a7e0..8eedd3a 100644
--- a/initial/pom.xml
+++ b/initial/pom.xml
@@ -25,10 +25,10 @@
org.springframework.boot
spring-boot-starter-webflux
-
- org.projectlombok
- lombok
+ org.springframework.boot
+ spring-boot-docker-compose
+ runtime
true
@@ -45,18 +45,7 @@
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
+
diff --git a/initial/src/main/java/com/example/demo/DemoApplication.java b/initial/src/main/java/com/example/demo/DemoApplication.java
new file mode 100644
index 0000000..e03ec75
--- /dev/null
+++ b/initial/src/main/java/com/example/demo/DemoApplication.java
@@ -0,0 +1,12 @@
+package com.example.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(DemoApplication.class, args);
+ }
+}
diff --git a/test/run.sh b/test/run.sh
deleted file mode 100755
index dff7e36..0000000
--- a/test/run.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/sh
-cd $(dirname $0)
-
-cd ../complete
-
-./mvnw clean package
-ret=$?
-if [ $ret -ne 0 ]; then
- exit $ret
-fi
-rm -rf target
-
-./gradlew build
-ret=$?
-if [ $ret -ne 0 ]; then
- exit $ret
-fi
-rm -rf build
-
-cd ../initial
-
-./mvnw clean compile
-ret=$?
-if [ $ret -ne 0 ]; then
- exit $ret
-fi
-rm -rf target
-
-./gradlew compileJava
-ret=$?
-if [ $ret -ne 0 ]; then
- exit $ret
-fi
-rm -rf build
-
-exit