Spring boot 4#1
Conversation
There was a problem hiding this comment.
Pull request overview
This PR upgrades the project to Spring Boot 4 and updates supporting infrastructure/configuration to align with the new reactive + observability stack (OpenTelemetry), while also refactoring Kafka integration and improving batch update behavior in the permission repository.
Changes:
- Upgrade build/dependencies to Spring Boot 4.0.2 and introduce/adjust version pinning & BOM usage.
- Replace reactive Kafka (reactor-kafka templates) with Spring Kafka (
@KafkaListener,KafkaTemplate) and add Kafka Testcontainers wiring for integration tests. - Switch tracing/metrics configuration toward OpenTelemetry/OTLP and adjust logging configuration (logback).
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
build.gradle |
Spring Boot 4 upgrade and broad dependency refactor/version pinning. |
src/main/resources/application.properties |
Reactive app mode + OTLP/propagation configuration changes. |
src/main/resources/application-local.properties |
Local profile disables OTLP exporters. |
src/main/resources/logback-spring.xml |
Adds rolling file + OpenTelemetry appender and profile rework. |
src/main/kotlin/com/softeno/template/app/kafka/config/KafkaConfig.kt |
Switches Kafka config from reactive templates to Spring Kafka beans with observation enabled. |
src/main/kotlin/com/softeno/template/app/kafka/KafkaSample.kt |
New Kafka sample producer/consumer using KafkaTemplate + @KafkaListener. |
src/main/kotlin/com/softeno/template/app/kafka/KafkaKeycloakController.kt |
Migrates Keycloak Kafka consumer to @KafkaListener + DTO co-location. |
src/test/kotlin/com/softeno/template/app/IntegrationTest.kt |
Adds Kafka Testcontainer + dynamic property registration; adds batch update nulling test. |
src/main/kotlin/com/softeno/template/app/permission/db/PermissionRepository.kt |
Batch insert/update binding changes to support setting nulls on update. |
src/main/kotlin/com/softeno/template/app/permission/service/PermissionService.kt |
Adjusts producer type/message model after Kafka refactor. |
src/main/kotlin/com/softeno/template/app/config/opentelementry/OpenTelemetryConfig.kt |
Adds OpenTelemetry appender installation + context propagation task decorator. |
AGENTS.md |
Adds automated-agent guidance document. |
docker-compose.yml / init_volumes.sh |
Adds local docker environment and volume init helper. |
src/test/resources/application.properties |
Test Kafka consumer default JSON type + removes hardcoded bootstrap servers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import org.springframework.kafka.annotation.KafkaListener | ||
| import org.springframework.stereotype.Controller | ||
| import reactor.core.publisher.Flux | ||
| import tools.jackson.databind.ObjectMapper |
There was a problem hiding this comment.
The import tools.jackson.databind.ObjectMapper looks incorrect and will fail compilation unless there is a custom tools.jackson package. This should likely be com.fasterxml.jackson.databind.ObjectMapper (and keep Jackson types consistently imported from com.fasterxml.jackson.*).
There was a problem hiding this comment.
it's jackson 3 with spring boot 4
|
|
||
| spec = if (value != null) { | ||
| spec.bind(paramName, value) | ||
| } else { | ||
| spec.bindNull(paramName, Any::class.java) |
There was a problem hiding this comment.
bindNull(paramName, Any::class.java) uses Any as the parameter type, which is unlikely to be a valid R2DBC codec target for the actual column type and can cause runtime binding errors when a value is null. Bind nulls using the column's Java type (e.g., from prop.javaField?.type / Kotlin return type) so the driver can encode it correctly.
| spec = if (value != null) { | |
| spec.bind(paramName, value) | |
| } else { | |
| spec.bindNull(paramName, Any::class.java) | |
| @Suppress("UNCHECKED_CAST") | |
| val javaType = prop.javaField!!.type as Class<Any> | |
| spec = if (value != null) { | |
| spec.bind(paramName, value) | |
| } else { | |
| spec.bindNull(paramName, javaType) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 27 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import org.springframework.kafka.annotation.KafkaListener | ||
| import org.springframework.stereotype.Controller | ||
| import reactor.core.publisher.Flux | ||
| import tools.jackson.databind.ObjectMapper | ||
|
|
There was a problem hiding this comment.
tools.jackson.databind.ObjectMapper does not appear anywhere else in this repository, and there is no tools.jackson package under src/main/kotlin. Unless a dependency provides that package, this import will not compile; typically Jackson's ObjectMapper is com.fasterxml.jackson.databind.ObjectMapper.
| val paramName = "${prop.name}$idx" | ||
|
|
||
| spec = if (value != null) { | ||
| spec.bind(paramName, value) | ||
| } else { | ||
| spec.bindNull(paramName, Any::class.java) | ||
| } |
There was a problem hiding this comment.
bindNull(paramName, Any::class.java) is too generic for R2DBC parameter binding and can fail because the driver often needs the concrete column type to encode NULLs. Consider binding nulls with the property/field Java type (e.g., prop.javaField!!.type) so the driver can infer the correct codec.
| KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker:9092,EXTERNAL://kafka-broker:9094 | ||
| KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT | ||
| ALLOW_PLAINTEXT_LISTENER: yes | ||
| KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker:9092,EXTERNAL://kafka-broker:9094 |
There was a problem hiding this comment.
Kafka is configured with an external port mapping (9094) and the app defaults to spring.kafka.bootstrap-servers=localhost:9094, but the broker advertises EXTERNAL://kafka-broker:9094. For clients running on the host, this advertised address is typically not resolvable and will break connections after the initial bootstrap. Consider advertising the host-accessible address for the EXTERNAL listener (e.g., localhost/IP) or aligning the app bootstrap servers to the advertised address when running in Docker networks.
| KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker:9092,EXTERNAL://kafka-broker:9094 | |
| KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT | |
| ALLOW_PLAINTEXT_LISTENER: yes | |
| KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker:9092,EXTERNAL://kafka-broker:9094 | |
| KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker:9092,EXTERNAL://localhost:9094 | |
| KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT | |
| ALLOW_PLAINTEXT_LISTENER: yes | |
| KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker:9092,EXTERNAL://localhost:9094 |
| kafka-broker: | ||
| image: bitnami/kafka:latest | ||
| container_name: kafka-broker |
There was a problem hiding this comment.
The compose file uses :latest tags (e.g., bitnami/kafka:latest, confluentinc/cp-kafka:latest). This makes environments non-reproducible and can break unexpectedly when upstream images change; pin these images to known-good versions.
No description provided.