Skip to content

Commit f9dbd97

Browse files
marijarinm.zharinovamfvanek
authored
Add reactive application with database and Kafka (#290)
* add tests * add autowired filter * fix tests and flows * fix mono code * lower branch coverage for reactive app to 0.5 * fix after merge master * delete some log details * done with Mono.fromCallable() * 3 versions to change headers but not working as expected * Fix reactive chain * Fix tests * Add BlockHound * Polish --------- Co-authored-by: m.zharinova <[email protected]> Co-authored-by: Ivan Vakhrushev <[email protected]>
1 parent 3724ff2 commit f9dbd97

39 files changed

+1772
-22
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ build/
88
*.iml
99
*.ipr
1010
out/
11+
12+
.kotlin

buildSrc/src/main/kotlin/sb-ot-demo.jacoco-rules.gradle.kts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ tasks {
3434
minimum = "0.93".toBigDecimal()
3535
}
3636
}
37-
rule {
38-
limit {
39-
counter = "BRANCH"
40-
value = "COVEREDRATIO"
41-
minimum = "0.66".toBigDecimal()
42-
}
43-
}
4437
}
4538
}
4639
}

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ include("spring-boot-3-demo-app")
44
include("common-internal-bom")
55
include("db-migrations")
66
include("spring-boot-3-demo-app-kotlin")
7+
include("spring-boot-3-demo-app-reactive")
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import org.junit.jupiter.api.DisplayName
1111
import org.junit.jupiter.api.Test
1212
import org.springframework.beans.factory.annotation.Autowired
1313

14-
class IndexesMaintenanceTest : TestBase() {
14+
internal class DatabaseStructureStaticAnalysisTest : TestBase() {
15+
1516
@Autowired
1617
private lateinit var checks: List<DatabaseCheckOnHost<out DbObject>>
1718

spring-boot-3-demo-app-kotlin/src/test/kotlin/io/github/mfvanek/spring/boot3/kotlin/test/controllers/TimeControllerTest.kt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@ import io.github.mfvanek.spring.boot3.kotlin.test.filters.TraceIdInResponseServl
44
import io.github.mfvanek.spring.boot3.kotlin.test.service.dto.toParsedDateTime
55
import io.github.mfvanek.spring.boot3.kotlin.test.support.KafkaInitializer
66
import io.github.mfvanek.spring.boot3.kotlin.test.support.TestBase
7-
import java.nio.charset.StandardCharsets
8-
import java.time.Duration
9-
import java.time.LocalDateTime
10-
import java.time.temporal.ChronoUnit
11-
import java.util.Locale
12-
import java.util.UUID
13-
import java.util.concurrent.BlockingQueue
14-
import java.util.concurrent.LinkedBlockingQueue
15-
import java.util.concurrent.TimeUnit
167
import org.apache.kafka.clients.CommonClientConfigs
178
import org.apache.kafka.clients.consumer.ConsumerConfig
189
import org.apache.kafka.clients.consumer.ConsumerRecord
@@ -38,12 +29,20 @@ import org.springframework.kafka.listener.MessageListener
3829
import org.springframework.kafka.test.utils.ContainerTestUtils
3930
import org.springframework.kafka.test.utils.KafkaTestUtils
4031
import org.testcontainers.shaded.org.awaitility.Awaitility
32+
import java.nio.charset.StandardCharsets
33+
import java.time.Duration
34+
import java.time.LocalDateTime
35+
import java.time.temporal.ChronoUnit
36+
import java.util.*
37+
import java.util.concurrent.ArrayBlockingQueue
38+
import java.util.concurrent.BlockingQueue
39+
import java.util.concurrent.TimeUnit
4140

4241
@ExtendWith(OutputCaptureExtension::class)
4342
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
4443
class TimeControllerTest : TestBase() {
4544
private lateinit var container: KafkaMessageListenerContainer<UUID, String>
46-
private val consumerRecords = LinkedBlockingQueue<ConsumerRecord<UUID, String>>()
45+
private val consumerRecords = ArrayBlockingQueue<ConsumerRecord<UUID, String>>(4)
4746

4847
@Autowired
4948
private lateinit var kafkaProperties: KafkaProperties
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
plugins {
2+
id("sb-ot-demo.java-conventions")
3+
id("sb-ot-demo.forbidden-apis")
4+
id("sb-ot-demo.docker")
5+
alias(libs.plugins.spring.boot.v3)
6+
id("io.freefair.lombok")
7+
}
8+
9+
dependencies {
10+
implementation(platform(project(":common-internal-bom")))
11+
implementation(platform(libs.springdoc.openapi))
12+
implementation(platform(libs.spring.boot.v3.dependencies))
13+
implementation(platform(libs.spring.cloud))
14+
15+
implementation("org.springframework.boot:spring-boot-starter-webflux")
16+
implementation("org.springframework.boot:spring-boot-starter-actuator")
17+
implementation("org.springframework.boot:spring-boot-starter-security")
18+
implementation("io.micrometer:micrometer-registry-prometheus")
19+
implementation("io.projectreactor:reactor-tools")
20+
implementation("org.springdoc:springdoc-openapi-starter-webflux-ui")
21+
22+
implementation("org.springframework.kafka:spring-kafka")
23+
24+
implementation("io.micrometer:micrometer-tracing-bridge-otel")
25+
implementation("io.opentelemetry:opentelemetry-exporter-otlp")
26+
27+
implementation("org.springframework.boot:spring-boot-starter-jdbc")
28+
implementation("org.postgresql:postgresql")
29+
implementation("com.zaxxer:HikariCP")
30+
implementation(project(":db-migrations"))
31+
implementation("org.liquibase:liquibase-core")
32+
implementation("com.github.blagerweij:liquibase-sessionlock")
33+
implementation(libs.datasource.micrometer)
34+
implementation(libs.logstash)
35+
36+
testImplementation("org.springframework.boot:spring-boot-starter-test")
37+
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
38+
testImplementation("org.testcontainers:postgresql")
39+
testImplementation("org.testcontainers:kafka")
40+
testImplementation("org.springframework.kafka:spring-kafka-test")
41+
testImplementation("org.awaitility:awaitility")
42+
testImplementation("io.github.mfvanek:pg-index-health-test-starter")
43+
testImplementation("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
44+
testImplementation("io.projectreactor:reactor-test")
45+
testImplementation("io.projectreactor.tools:blockhound:1.0.13.RELEASE")
46+
}
47+
48+
tasks {
49+
withType<Test>().all {
50+
jvmArgs("-XX:+AllowRedefinitionToAddDeleteMethods")
51+
}
52+
53+
jacocoTestCoverageVerification {
54+
dependsOn(jacocoTestReport)
55+
violationRules {
56+
rule {
57+
limit {
58+
counter = "BRANCH"
59+
value = "COVEREDRATIO"
60+
minimum = "0.50".toBigDecimal()
61+
}
62+
}
63+
}
64+
}
65+
}
66+
67+
val coverageExcludeList = listOf("**/*Application.class")
68+
listOf(JacocoCoverageVerification::class, JacocoReport::class).forEach { taskType ->
69+
tasks.withType(taskType) {
70+
afterEvaluate {
71+
classDirectories.setFrom(
72+
files(
73+
classDirectories.files.map { file ->
74+
fileTree(file).apply {
75+
exclude(coverageExcludeList)
76+
}
77+
}
78+
)
79+
)
80+
}
81+
}
82+
}
83+
84+
85+
springBoot {
86+
buildInfo()
87+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) 2020-2025. Ivan Vakhrushev and others.
3+
* https://github.com/mfvanek/spring-boot-open-telemetry-demo
4+
*
5+
* Licensed under the Apache License 2.0
6+
*/
7+
8+
package io.github.mfvanek.spring.boot3.reactive;
9+
10+
import org.springframework.boot.SpringApplication;
11+
import org.springframework.boot.autoconfigure.SpringBootApplication;
12+
import reactor.tools.agent.ReactorDebugAgent;
13+
14+
@SpringBootApplication
15+
public class Application {
16+
17+
public static void main(String[] args) {
18+
ReactorDebugAgent.init();
19+
SpringApplication.run(Application.class, args);
20+
}
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2020-2025. Ivan Vakhrushev and others.
3+
* https://github.com/mfvanek/spring-boot-open-telemetry-demo
4+
*
5+
* Licensed under the Apache License 2.0
6+
*/
7+
8+
package io.github.mfvanek.spring.boot3.reactive.config;
9+
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
13+
import java.time.Clock;
14+
15+
@Configuration(proxyBeanMethods = false)
16+
public class ClockConfig {
17+
18+
@Bean
19+
public Clock clock() {
20+
return Clock.systemUTC();
21+
}
22+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2020-2025. Ivan Vakhrushev and others.
3+
* https://github.com/mfvanek/spring-boot-open-telemetry-demo
4+
*
5+
* Licensed under the Apache License 2.0
6+
*/
7+
8+
package io.github.mfvanek.spring.boot3.reactive.config;
9+
10+
import io.opentelemetry.sdk.common.export.RetryPolicy;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpHttpSpanExporterBuilderCustomizer;
13+
import org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpTracingAutoConfiguration;
14+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
15+
import org.springframework.context.annotation.Bean;
16+
import org.springframework.context.annotation.Configuration;
17+
18+
@AutoConfigureBefore(OtlpTracingAutoConfiguration.class)
19+
@Configuration(proxyBeanMethods = false)
20+
class OpenTelemetryConfig {
21+
22+
@Bean
23+
OtlpHttpSpanExporterBuilderCustomizer otelJaegerHttpSpanExporterBuilderCustomizer(
24+
@Value("${management.otlp.tracing.retry.max-attempts:2}") int maxAttempts
25+
) {
26+
return builder -> builder.setRetryPolicy(
27+
RetryPolicy.builder()
28+
.setMaxAttempts(maxAttempts)
29+
.build()
30+
);
31+
}
32+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2020-2025. Ivan Vakhrushev and others.
3+
* https://github.com/mfvanek/spring-boot-open-telemetry-demo
4+
*
5+
* Licensed under the Apache License 2.0
6+
*/
7+
8+
package io.github.mfvanek.spring.boot3.reactive.config;
9+
10+
import lombok.SneakyThrows;
11+
import org.springframework.context.annotation.Bean;
12+
import org.springframework.context.annotation.Configuration;
13+
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
14+
import org.springframework.security.config.web.server.ServerHttpSecurity;
15+
import org.springframework.security.web.server.SecurityWebFilterChain;
16+
17+
@Configuration(proxyBeanMethods = false)
18+
@EnableWebFluxSecurity
19+
public class SecurityConfig {
20+
21+
@Bean
22+
@SneakyThrows
23+
public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
24+
http
25+
.authorizeExchange(exchanges -> exchanges.anyExchange().permitAll());
26+
return http.build();
27+
}
28+
}

0 commit comments

Comments
 (0)