diff --git a/.gemini/settings.json b/.gemini/settings.json new file mode 100644 index 00000000..90aaf3e5 --- /dev/null +++ b/.gemini/settings.json @@ -0,0 +1,5 @@ +{ + "general": { + "previewFeatures": true + } +} diff --git a/GeminiBerichte/GatewayDependencies.txt b/GeminiBerichte/GatewayDependencies.txt deleted file mode 100644 index 38ba99f4..00000000 --- a/GeminiBerichte/GatewayDependencies.txt +++ /dev/null @@ -1,687 +0,0 @@ -stefan-mo@Li-Desk-Mo:~/WsMeldestelle/Meldestelle$ ./gradlew :backend:infrastructure:gateway:dependencies --configuration compileClasspath -Type-safe project accessors is an incubating feature. - -> Task :backend:infrastructure:gateway:dependencies - ------------------------------------------------------------- -Project ':backend:infrastructure:gateway' ------------------------------------------------------------- - -compileClasspath - Compile classpath for 'main'. -+--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 -| +--- org.jetbrains:annotations:13.0 -> 23.0.0 -| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 -> 2.3.0 (c) -| \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0 -> 2.3.0 (c) -+--- org.springframework.boot:spring-boot-dependencies:4.0.1 -| +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.1 (c) -| +--- com.fasterxml.jackson.module:jackson-module-kotlin:2.20.1 (c) -| +--- org.jetbrains.kotlin:kotlin-stdlib:2.2.21 -> 2.3.0 (c) -| +--- org.springframework.security:spring-security-oauth2-jose:7.0.2 (c) -| +--- ch.qos.logback:logback-classic:1.5.22 (c) -| +--- ch.qos.logback:logback-core:1.5.22 (c) -| +--- org.springframework.boot:spring-boot-starter-actuator:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-starter-data-redis:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-starter-oauth2-resource-server:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-starter-security:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-starter-webflux:4.0.1 (c) -| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 -> 1.7.3 (c) -| +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 (c) -| +--- org.flywaydb:flyway-core:11.14.1 -> 11.19.1 (c) -| +--- org.flywaydb:flyway-database-postgresql:11.14.1 -> 11.19.1 (c) -| +--- com.zaxxer:HikariCP:7.0.2 (c) -| +--- jakarta.annotation:jakarta.annotation-api:3.0.0 (c) -| +--- org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.10.2 (c) -| +--- org.slf4j:slf4j-api:2.0.17 (c) -| +--- com.fasterxml.jackson.core:jackson-annotations:2.20 (c) -| +--- com.fasterxml.jackson.core:jackson-core:2.20.1 (c) -| +--- com.fasterxml.jackson.core:jackson-databind:2.20.1 (c) -| +--- org.jetbrains.kotlin:kotlin-reflect:2.2.21 -> 2.3.0 (c) -| +--- org.springframework.security:spring-security-core:7.0.2 (c) -| +--- org.springframework.security:spring-security-oauth2-core:7.0.2 (c) -| +--- org.springframework:spring-core:7.0.2 (c) -| +--- org.springframework.boot:spring-boot-starter:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-actuator-autoconfigure:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-health:4.0.1 (c) -| +--- io.micrometer:micrometer-observation:1.16.1 (c) -| +--- io.micrometer:micrometer-jakarta9:1.16.1 (c) -| +--- org.springframework.boot:spring-boot-data-redis:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-security-oauth2-resource-server:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-security:4.0.1 (c) -| +--- org.springframework:spring-aop:7.0.2 (c) -| +--- org.springframework.boot:spring-boot-starter-jackson:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-starter-reactor-netty:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-webflux:4.0.1 (c) -| +--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.9.0 (c) -| +--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 (c) -| +--- io.projectreactor:reactor-core:3.8.1 (c) -| +--- org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.10.2 (c) -| +--- org.springframework.security:spring-security-crypto:7.0.2 (c) -| +--- org.springframework:spring-beans:7.0.2 (c) -| +--- org.springframework:spring-context:7.0.2 (c) -| +--- org.springframework:spring-expression:7.0.2 (c) -| +--- org.springframework:spring-web:7.0.2 (c) -| +--- commons-logging:commons-logging:1.3.5 (c) -| +--- org.jspecify:jspecify:1.0.0 (c) -| +--- org.springframework.boot:spring-boot-starter-logging:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-autoconfigure:4.0.1 (c) -| +--- org.yaml:snakeyaml:2.5 (c) -| +--- org.springframework.boot:spring-boot-micrometer-metrics:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-actuator:4.0.1 (c) -| +--- org.springframework.boot:spring-boot:4.0.1 (c) -| +--- io.micrometer:micrometer-commons:1.16.1 (c) -| +--- io.micrometer:micrometer-core:1.16.1 (c) -| +--- org.springframework.boot:spring-boot-data-commons:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-transaction:4.0.1 (c) -| +--- io.lettuce:lettuce-core:6.8.1.RELEASE -> 7.2.1.RELEASE (c) -| +--- org.springframework.data:spring-data-redis:4.0.1 (c) -| +--- org.springframework.security:spring-security-oauth2-resource-server:7.0.2 (c) -| +--- org.springframework.security:spring-security-config:7.0.2 (c) -| +--- org.springframework.security:spring-security-web:7.0.2 (c) -| +--- org.springframework.boot:spring-boot-jackson:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-reactor:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-reactor-netty:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-http-codec:4.0.1 (c) -| +--- org.springframework:spring-webflux:7.0.2 (c) -| +--- org.springframework.boot:spring-boot-configuration-processor:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-starter-validation:4.0.1 (c) -| +--- io.projectreactor.addons:reactor-extra:3.6.0 (c) -| +--- com.google.code.gson:gson:2.13.2 (c) -| +--- org.apache.httpcomponents:httpcore:4.4.16 (c) -| +--- org.springframework.boot:spring-boot-starter-cache:4.0.1 (c) -| +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0 (c) -| +--- io.netty:netty-common:4.2.9.Final (c) -| +--- io.netty:netty-handler:4.2.9.Final (c) -| +--- io.netty:netty-transport:4.2.9.Final (c) -| +--- io.netty:netty-resolver-dns:4.2.9.Final (c) -| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21 -> 2.3.0 (c) -| +--- commons-codec:commons-codec:1.19.0 (c) -| +--- org.springframework.boot:spring-boot-restclient:4.0.1 (c) -| +--- org.reactivestreams:reactive-streams:1.0.4 (c) -| +--- org.apache.logging.log4j:log4j-to-slf4j:2.25.3 (c) -| +--- org.slf4j:jul-to-slf4j:2.0.17 (c) -| +--- org.springframework.boot:spring-boot-micrometer-observation:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-persistence:4.0.1 (c) -| +--- org.springframework.data:spring-data-commons:4.0.1 (c) -| +--- org.springframework:spring-tx:7.0.2 (c) -| +--- org.springframework.data:spring-data-keyvalue:4.0.1 (c) -| +--- org.springframework:spring-oxm:7.0.2 (c) -| +--- org.springframework:spring-context-support:7.0.2 (c) -| +--- tools.jackson.core:jackson-databind:3.0.3 (c) -| +--- org.springframework.boot:spring-boot-web-server:4.0.1 (c) -| +--- io.projectreactor.netty:reactor-netty-http:1.3.1 (c) -| +--- org.springframework.boot:spring-boot-validation:4.0.1 (c) -| +--- org.springframework.boot:spring-boot-cache:4.0.1 (c) -| +--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0 (c) -| +--- io.netty:netty-resolver:4.2.9.Final (c) -| +--- io.netty:netty-buffer:4.2.9.Final (c) -| +--- io.netty:netty-transport-native-unix-common:4.2.9.Final (c) -| +--- io.netty:netty-codec-base:4.2.9.Final (c) -| +--- io.netty:netty-codec-dns:4.2.9.Final (c) -| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21 -> 2.3.0 (c) -| +--- org.springframework.boot:spring-boot-http-client:4.0.1 (c) -| +--- org.apache.logging.log4j:log4j-api:2.25.3 (c) -| +--- tools.jackson.core:jackson-core:3.0.3 (c) -| +--- io.netty:netty-codec-http:4.2.9.Final (c) -| +--- io.netty:netty-codec-http2:4.2.9.Final (c) -| +--- io.netty:netty-codec-http3:4.2.9.Final (c) -| +--- io.netty:netty-resolver-dns-native-macos:4.2.9.Final (c) -| +--- io.netty:netty-transport-native-epoll:4.2.9.Final (c) -| +--- io.projectreactor.netty:reactor-netty-core:1.3.1 (c) -| +--- org.apache.tomcat.embed:tomcat-embed-el:11.0.15 (c) -| +--- org.hibernate.validator:hibernate-validator:9.0.1.Final (c) -| +--- io.netty:netty-codec-compression:4.2.9.Final (c) -| +--- io.netty:netty-codec-classes-quic:4.2.9.Final (c) -| +--- io.netty:netty-resolver-dns-classes-macos:4.2.9.Final (c) -| +--- io.netty:netty-transport-classes-epoll:4.2.9.Final (c) -| +--- io.netty:netty-handler-proxy:4.2.9.Final (c) -| +--- jakarta.validation:jakarta.validation-api:3.1.1 (c) -| +--- org.jboss.logging:jboss-logging:3.6.1.Final (c) -| +--- com.fasterxml:classmate:1.7.1 (c) -| \--- io.netty:netty-codec-socks:4.2.9.Final (c) -+--- org.springframework.cloud:spring-cloud-dependencies:2025.1.0 -| +--- org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-starter-gateway-server-webflux:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-starter-consul-discovery:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-starter:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-circuitbreaker-resilience4j:5.0.0 (c) -| +--- io.github.resilience4j:resilience4j-circuitbreaker:2.3.0 (c) -| +--- io.github.resilience4j:resilience4j-timelimiter:2.3.0 (c) -| +--- org.springframework.cloud:spring-cloud-gateway-server-webflux:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-starter-consul:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-consul-discovery:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-starter-loadbalancer:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-context:5.0.0 (c) -| +--- org.springframework.cloud:spring-cloud-commons:5.0.0 (c) -| +--- io.github.resilience4j:resilience4j-spring-boot3:2.3.0 (c) -| +--- io.github.resilience4j:resilience4j-core:2.3.0 (c) -| +--- org.springframework.cloud:spring-cloud-consul-core:5.0.0 (c) -| +--- com.ecwid.consul:consul-api:1.4.5 (c) -| +--- org.springframework.cloud:spring-cloud-loadbalancer:5.0.0 (c) -| +--- io.github.resilience4j:resilience4j-spring6:2.3.0 (c) -| +--- io.github.resilience4j:resilience4j-annotations:2.3.0 (c) -| +--- io.github.resilience4j:resilience4j-consumer:2.3.0 (c) -| +--- io.github.resilience4j:resilience4j-ratelimiter:2.3.0 (c) -| +--- io.github.resilience4j:resilience4j-retry:2.3.0 (c) -| \--- io.github.resilience4j:resilience4j-micrometer:2.3.0 (c) -+--- project :core:core-utils -| +--- project :core:core-domain -| | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 -> 1.7.3 -| | | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.3 -> 1.9.0 -| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0 -| | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0 (c) -| | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.9.0 (c) -| | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 -> 1.7.3 (c) -| | | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0 (c) -| | | +--- org.jetbrains.kotlin:kotlin-stdlib:2.2.0 -> 2.3.0 (*) -| | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0 -| | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.9.0 -| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0 (*) -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.2.0 -> 2.3.0 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-datetime:0.7.1 -| | | \--- org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.7.1 -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.1.20 -> 2.3.0 (*) -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (*) -| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 -> 1.7.3 (*) -| +--- org.jetbrains.kotlinx:kotlinx-datetime:0.7.1 (*) -| +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 -| | \--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 -| | +--- org.jetbrains:annotations:23.0.0 -| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2 (c) -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 (c) -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.10.2 (c) -| | | \--- org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.10.2 (c) -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.1.0 -> 2.3.0 (*) -| +--- com.ionspin.kotlin:bignum:0.3.10 -| | \--- com.ionspin.kotlin:bignum-jvm:0.3.10 -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.3.0 (*) -| +--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (*) -| +--- project :platform:platform-dependencies -| | +--- project :platform:platform-bom -| | | +--- org.springframework.boot:spring-boot-dependencies:4.0.1 (*) -| | | +--- org.springframework.cloud:spring-cloud-dependencies:2025.1.0 (*) -| | | +--- org.jetbrains.kotlin:kotlin-bom:2.3.0 -| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (c) -| | | | +--- org.jetbrains.kotlin:kotlin-reflect:2.3.0 (c) -| | | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.3.0 (c) -| | | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.3.0 (c) -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 (*) -| | | +--- com.ionspin.kotlin:bignum:0.3.10 (c) -| | | +--- org.springframework.cloud:spring-cloud-starter-consul-discovery -> 5.0.0 (c) -| | | +--- io.github.oshai:kotlin-logging-jvm:7.0.13 (c) -| | | +--- jakarta.annotation:jakarta.annotation-api:3.0.0 (c) -| | | +--- ch.qos.logback:logback-classic:1.5.22 (c) -| | | +--- org.jetbrains.exposed:exposed-core:0.61.0 (c) -| | | +--- org.jetbrains.exposed:exposed-dao:0.61.0 (c) -| | | +--- org.jetbrains.exposed:exposed-jdbc:0.61.0 (c) -| | | +--- org.jetbrains.exposed:exposed-kotlin-datetime:0.61.0 (c) -| | | +--- org.flywaydb:flyway-core:11.19.1 (c) -| | | +--- org.flywaydb:flyway-database-postgresql:11.19.1 (c) -| | | +--- com.zaxxer:HikariCP:7.0.2 (c) -| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 -> 1.7.3 (c) -| | | +--- org.jetbrains.kotlinx:kotlinx-datetime:0.7.1 (c) -| | | +--- com.fasterxml.jackson.module:jackson-module-kotlin -> 2.20.1 (c) -| | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310 -> 2.20.1 (c) -| | | \--- io.lettuce:lettuce-core:7.2.1.RELEASE (c) -| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0 -> 1.7.3 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-datetime:0.7.1 (*) -| | +--- io.github.oshai:kotlin-logging-jvm:7.0.13 -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.1.21 -> 2.3.0 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.10.2 -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 (*) -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 (*) -| | | +--- io.projectreactor:reactor-core:3.4.1 -> 3.8.1 -| | | | +--- org.reactivestreams:reactive-streams:1.0.4 -| | | | \--- org.jspecify:jspecify:1.0.0 -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.10.2 -| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 (*) -| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2 (*) -| | | | +--- org.reactivestreams:reactive-streams:1.0.3 -> 1.0.4 -| | | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.1.0 -> 2.3.0 (*) -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.1.0 -> 2.3.0 (*) -| | +--- ch.qos.logback:logback-classic:1.5.22 -| | | +--- ch.qos.logback:logback-core:1.5.22 -| | | \--- org.slf4j:slf4j-api:2.0.17 -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (*) -| +--- org.jetbrains.exposed:exposed-core:0.61.0 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.3.0 (*) -| | +--- org.jetbrains.kotlin:kotlin-reflect:2.0.0 -> 2.3.0 -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1 -> 1.10.2 (*) -| | \--- org.slf4j:slf4j-api:2.0.9 -> 2.0.17 -| +--- org.jetbrains.exposed:exposed-dao:0.61.0 -| | +--- org.jetbrains.exposed:exposed-core:0.61.0 (*) -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.3.0 (*) -| +--- org.jetbrains.exposed:exposed-jdbc:0.61.0 -| | +--- org.jetbrains.exposed:exposed-core:0.61.0 (*) -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.3.0 (*) -| +--- org.jetbrains.exposed:exposed-kotlin-datetime:0.61.0 -| | +--- org.jetbrains.exposed:exposed-core:0.61.0 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.2 -> 0.7.1 (*) -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.3.0 (*) -| +--- org.flywaydb:flyway-core:11.19.1 -| | \--- com.fasterxml.jackson.core:jackson-databind:2.19.1 -> 2.20.1 -| | +--- com.fasterxml.jackson.core:jackson-annotations:2.20 -| | +--- com.fasterxml.jackson.core:jackson-core:2.20.1 -| | | \--- com.fasterxml.jackson:jackson-bom:2.20.1 -| | | +--- com.fasterxml.jackson.core:jackson-annotations:2.20 (c) -| | | +--- com.fasterxml.jackson.core:jackson-core:2.20.1 (c) -| | | +--- com.fasterxml.jackson.core:jackson-databind:2.20.1 (c) -| | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.1 (c) -| | | \--- com.fasterxml.jackson.module:jackson-module-kotlin:2.20.1 (c) -| | \--- com.fasterxml.jackson:jackson-bom:2.20.1 (*) -| +--- org.flywaydb:flyway-database-postgresql:11.19.1 -| | \--- org.flywaydb:flyway-core:11.19.1 (*) -| +--- com.zaxxer:HikariCP:7.0.2 -| | \--- org.slf4j:slf4j-api:2.0.17 -| +--- org.springframework.cloud:spring-cloud-starter-consul-discovery -> 5.0.0 -| | +--- org.springframework.cloud:spring-cloud-starter-consul:5.0.0 -| | | +--- org.springframework.cloud:spring-cloud-starter:5.0.0 -| | | | +--- org.springframework.boot:spring-boot-starter:4.0.0 -> 4.0.1 -| | | | | +--- org.springframework.boot:spring-boot-starter-logging:4.0.1 -| | | | | | +--- ch.qos.logback:logback-classic:1.5.22 (*) -| | | | | | +--- org.apache.logging.log4j:log4j-to-slf4j:2.25.3 -| | | | | | | +--- org.apache.logging.log4j:log4j-api:2.25.3 -| | | | | | | | +--- org.jspecify:jspecify:1.0.0 -| | | | | | | | +--- biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0 -| | | | | | | | | +--- org.osgi:org.osgi.resource:1.0.0 -| | | | | | | | | \--- org.osgi:org.osgi.service.serviceloader:1.0.0 -| | | | | | | | +--- com.google.errorprone:error_prone_annotations:2.38.0 -> 2.41.0 -| | | | | | | | +--- org.osgi:org.osgi.annotation.bundle:2.0.0 -| | | | | | | | | \--- org.osgi:org.osgi.annotation.versioning:1.1.2 -| | | | | | | | \--- org.osgi:org.osgi.annotation.versioning:1.1.2 -| | | | | | | +--- org.slf4j:slf4j-api:2.0.17 -| | | | | | | +--- org.jspecify:jspecify:1.0.0 -| | | | | | | +--- biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0 (*) -| | | | | | | +--- com.google.errorprone:error_prone_annotations:2.38.0 -> 2.41.0 -| | | | | | | +--- org.osgi:org.osgi.annotation.bundle:2.0.0 (*) -| | | | | | | \--- org.osgi:org.osgi.annotation.versioning:1.1.2 -| | | | | | \--- org.slf4j:jul-to-slf4j:2.0.17 -| | | | | | \--- org.slf4j:slf4j-api:2.0.17 -| | | | | +--- org.springframework.boot:spring-boot-autoconfigure:4.0.1 -| | | | | | \--- org.springframework.boot:spring-boot:4.0.1 -| | | | | | +--- org.springframework:spring-core:7.0.2 -| | | | | | | +--- commons-logging:commons-logging:1.3.5 -| | | | | | | \--- org.jspecify:jspecify:1.0.0 -| | | | | | \--- org.springframework:spring-context:7.0.2 -| | | | | | +--- org.springframework:spring-aop:7.0.2 -| | | | | | | +--- org.springframework:spring-beans:7.0.2 -| | | | | | | | \--- org.springframework:spring-core:7.0.2 (*) -| | | | | | | \--- org.springframework:spring-core:7.0.2 (*) -| | | | | | +--- org.springframework:spring-beans:7.0.2 (*) -| | | | | | +--- org.springframework:spring-core:7.0.2 (*) -| | | | | | +--- org.springframework:spring-expression:7.0.2 -| | | | | | | \--- org.springframework:spring-core:7.0.2 (*) -| | | | | | \--- io.micrometer:micrometer-observation:1.16.1 -| | | | | | +--- org.jspecify:jspecify:1.0.0 -| | | | | | \--- io.micrometer:micrometer-commons:1.16.1 -| | | | | | \--- org.jspecify:jspecify:1.0.0 -| | | | | +--- jakarta.annotation:jakarta.annotation-api:3.0.0 -| | | | | \--- org.yaml:snakeyaml:2.5 -| | | | +--- org.springframework.cloud:spring-cloud-context:5.0.0 -| | | | | \--- org.springframework.security:spring-security-crypto:7.0.0 -> 7.0.2 -| | | | +--- org.springframework.cloud:spring-cloud-commons:5.0.0 -| | | | | \--- org.springframework.security:spring-security-crypto:7.0.0 -> 7.0.2 -| | | | \--- org.bouncycastle:bcprov-jdk18on:1.81 -| | | +--- org.springframework.cloud:spring-cloud-consul-core:5.0.0 -| | | | +--- org.springframework.boot:spring-boot-starter-validation:4.0.0 -> 4.0.1 -| | | | | +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| | | | | \--- org.springframework.boot:spring-boot-validation:4.0.1 -| | | | | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | | | | +--- org.apache.tomcat.embed:tomcat-embed-el:11.0.15 -| | | | | \--- org.hibernate.validator:hibernate-validator:9.0.1.Final -| | | | | +--- jakarta.validation:jakarta.validation-api:3.1.1 -| | | | | +--- org.jboss.logging:jboss-logging:3.6.1.Final -| | | | | \--- com.fasterxml:classmate:1.7.0 -> 1.7.1 -| | | | \--- org.springframework.boot:spring-boot-restclient:4.0.0 -> 4.0.1 -| | | | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | | | \--- org.springframework.boot:spring-boot-http-client:4.0.1 -| | | | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | | | \--- org.springframework:spring-web:7.0.2 -| | | | +--- org.springframework:spring-beans:7.0.2 (*) -| | | | +--- org.springframework:spring-core:7.0.2 (*) -| | | | \--- io.micrometer:micrometer-observation:1.16.1 (*) -| | | +--- com.ecwid.consul:consul-api:1.4.5 -| | | +--- com.google.code.gson:gson:2.13.2 -| | | | \--- com.google.errorprone:error_prone_annotations:2.41.0 -| | | +--- org.apache.httpcomponents:httpclient:4.5.14 -| | | | +--- org.apache.httpcomponents:httpcore:4.4.16 -| | | | +--- commons-logging:commons-logging:1.2 -> 1.3.5 -| | | | \--- commons-codec:commons-codec:1.11 -> 1.19.0 -| | | \--- org.apache.httpcomponents:httpcore:4.4.16 -| | +--- org.springframework.cloud:spring-cloud-consul-discovery:5.0.0 -| | | \--- org.springframework.cloud:spring-cloud-consul-core:5.0.0 (*) -| | \--- org.springframework.cloud:spring-cloud-starter-loadbalancer:5.0.0 -| | +--- org.springframework.cloud:spring-cloud-starter:5.0.0 (*) -| | +--- org.springframework.cloud:spring-cloud-loadbalancer:5.0.0 -| | | +--- org.springframework.cloud:spring-cloud-commons:5.0.0 (*) -| | | +--- org.springframework.cloud:spring-cloud-context:5.0.0 (*) -| | | +--- io.projectreactor:reactor-core:3.8.0 -> 3.8.1 (*) -| | | \--- io.projectreactor.addons:reactor-extra:3.6.0 -| | | \--- io.projectreactor:reactor-core:3.8.0 -> 3.8.1 (*) -| | +--- org.springframework.boot:spring-boot-starter-cache:4.0.0 -> 4.0.1 -| | | +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| | | \--- org.springframework.boot:spring-boot-cache:4.0.1 -| | | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | | \--- org.springframework:spring-context-support:7.0.2 -| | | +--- org.springframework:spring-beans:7.0.2 (*) -| | | +--- org.springframework:spring-context:7.0.2 (*) -| | | \--- org.springframework:spring-core:7.0.2 (*) -| | \--- com.stoyanr:evictor:1.0.0 -| +--- io.github.oshai:kotlin-logging-jvm:7.0.13 (*) -| +--- jakarta.annotation:jakarta.annotation-api:3.0.0 -| +--- com.fasterxml.jackson.module:jackson-module-kotlin -> 2.20.1 -| | +--- com.fasterxml.jackson.core:jackson-databind:2.20.1 (*) -| | +--- com.fasterxml.jackson.core:jackson-annotations:2.20 -| | +--- org.jetbrains.kotlin:kotlin-reflect:2.0.21 -> 2.3.0 (*) -| | \--- com.fasterxml.jackson:jackson-bom:2.20.1 (*) -| \--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310 -> 2.20.1 -| +--- com.fasterxml.jackson.core:jackson-annotations:2.20 -| +--- com.fasterxml.jackson.core:jackson-core:2.20.1 (*) -| +--- com.fasterxml.jackson.core:jackson-databind:2.20.1 (*) -| \--- com.fasterxml.jackson:jackson-bom:2.20.1 (*) -+--- project :platform:platform-dependencies (*) -+--- project :backend:infrastructure:monitoring:monitoring-client -| \--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (*) -+--- org.springframework.boot:spring-boot-starter-webflux -> 4.0.1 -| +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| +--- org.springframework.boot:spring-boot-starter-jackson:4.0.1 -| | +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| | \--- org.springframework.boot:spring-boot-jackson:4.0.1 -| | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | \--- tools.jackson.core:jackson-databind:3.0.3 -| | +--- com.fasterxml.jackson.core:jackson-annotations:2.20 -| | +--- tools.jackson.core:jackson-core:3.0.3 -| | | \--- tools.jackson:jackson-bom:3.0.3 -| | | +--- com.fasterxml.jackson.core:jackson-annotations:2.20 (c) -| | | +--- tools.jackson.core:jackson-core:3.0.3 (c) -| | | \--- tools.jackson.core:jackson-databind:3.0.3 (c) -| | \--- tools.jackson:jackson-bom:3.0.3 (*) -| +--- org.springframework.boot:spring-boot-starter-reactor-netty:4.0.1 -| | +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| | +--- org.springframework.boot:spring-boot-reactor:4.0.1 -| | | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | | \--- io.projectreactor:reactor-core:3.8.1 (*) -| | \--- org.springframework.boot:spring-boot-reactor-netty:4.0.1 -| | +--- org.springframework.boot:spring-boot-web-server:4.0.1 -| | | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | | \--- org.springframework:spring-web:7.0.2 (*) -| | +--- io.projectreactor.netty:reactor-netty-http:1.3.1 -| | | +--- io.netty:netty-codec-http:4.2.7.Final -> 4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-buffer:4.2.9.Final -| | | | | \--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-transport:4.2.9.Final -| | | | | +--- io.netty:netty-common:4.2.9.Final -| | | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | | \--- io.netty:netty-resolver:4.2.9.Final -| | | | | \--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-codec-base:4.2.9.Final -| | | | | +--- io.netty:netty-common:4.2.9.Final -| | | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | | \--- io.netty:netty-transport:4.2.9.Final (*) -| | | | +--- io.netty:netty-codec-compression:4.2.9.Final -| | | | | +--- io.netty:netty-common:4.2.9.Final -| | | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | | \--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | | \--- io.netty:netty-handler:4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-resolver:4.2.9.Final (*) -| | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport-native-unix-common:4.2.9.Final -| | | | | +--- io.netty:netty-common:4.2.9.Final -| | | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | | \--- io.netty:netty-transport:4.2.9.Final (*) -| | | | \--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | +--- io.netty:netty-codec-http2:4.2.7.Final -> 4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | +--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | | +--- io.netty:netty-handler:4.2.9.Final (*) -| | | | \--- io.netty:netty-codec-http:4.2.9.Final (*) -| | | +--- io.netty:netty-codec-http3:4.2.7.Final -> 4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | +--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | | +--- io.netty:netty-codec-http:4.2.9.Final (*) -| | | | +--- io.netty:netty-codec-compression:4.2.9.Final (*) -| | | | +--- io.netty:netty-handler:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport-native-unix-common:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | +--- io.netty:netty-resolver:4.2.9.Final (*) -| | | | \--- io.netty:netty-codec-classes-quic:4.2.9.Final -| | | +--- io.netty:netty-resolver-dns:4.2.7.Final -> 4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | +--- io.netty:netty-resolver:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | +--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | | +--- io.netty:netty-codec-dns:4.2.9.Final -| | | | | +--- io.netty:netty-common:4.2.9.Final -| | | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | | \--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | | \--- io.netty:netty-handler:4.2.9.Final (*) -| | | +--- io.netty:netty-resolver-dns-native-macos:4.2.7.Final -> 4.2.9.Final -| | | | \--- io.netty:netty-resolver-dns-classes-macos:4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-resolver-dns:4.2.9.Final (*) -| | | | \--- io.netty:netty-transport-native-unix-common:4.2.9.Final (*) -| | | +--- io.netty:netty-transport-native-epoll:4.2.7.Final -> 4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport-native-unix-common:4.2.9.Final (*) -| | | | \--- io.netty:netty-transport-classes-epoll:4.2.9.Final -| | | | +--- io.netty:netty-common:4.2.9.Final -| | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | \--- io.netty:netty-transport-native-unix-common:4.2.9.Final (*) -| | | +--- io.projectreactor.netty:reactor-netty-core:1.3.1 -| | | | +--- io.netty:netty-handler:4.2.7.Final -> 4.2.9.Final (*) -| | | | +--- io.netty:netty-handler-proxy:4.2.7.Final -> 4.2.9.Final -| | | | | +--- io.netty:netty-common:4.2.9.Final -| | | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | | +--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | | | +--- io.netty:netty-codec-socks:4.2.9.Final -| | | | | | +--- io.netty:netty-common:4.2.9.Final -| | | | | | +--- io.netty:netty-buffer:4.2.9.Final (*) -| | | | | | +--- io.netty:netty-transport:4.2.9.Final (*) -| | | | | | \--- io.netty:netty-codec-base:4.2.9.Final (*) -| | | | | +--- io.netty:netty-codec-http:4.2.9.Final (*) -| | | | | \--- io.netty:netty-handler:4.2.9.Final (*) -| | | | +--- io.netty:netty-resolver-dns:4.2.7.Final -> 4.2.9.Final (*) -| | | | +--- io.netty:netty-resolver-dns-native-macos:4.2.7.Final -> 4.2.9.Final (*) -| | | | +--- io.netty:netty-transport-native-epoll:4.2.7.Final -> 4.2.9.Final (*) -| | | | +--- io.projectreactor:reactor-core:3.8.1 (*) -| | | | \--- org.jspecify:jspecify:1.0.0 -| | | +--- io.projectreactor:reactor-core:3.8.1 (*) -| | | \--- org.jspecify:jspecify:1.0.0 -| | \--- org.springframework:spring-web:7.0.2 (*) -| \--- org.springframework.boot:spring-boot-webflux:4.0.1 -| +--- org.springframework.boot:spring-boot:4.0.1 (*) -| +--- org.springframework.boot:spring-boot-http-codec:4.0.1 -| | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | \--- org.springframework:spring-web:7.0.2 (*) -| \--- org.springframework:spring-webflux:7.0.2 -| +--- org.springframework:spring-beans:7.0.2 (*) -| +--- org.springframework:spring-core:7.0.2 (*) -| +--- org.springframework:spring-web:7.0.2 (*) -| \--- io.projectreactor:reactor-core:3.8.1 (*) -+--- org.springframework.cloud:spring-cloud-starter-gateway-server-webflux -> 5.0.0 -| +--- org.springframework.cloud:spring-cloud-starter:5.0.0 (*) -| +--- org.springframework.cloud:spring-cloud-gateway-server-webflux:5.0.0 -| | +--- org.springframework.boot:spring-boot-starter:4.0.0 -> 4.0.1 (*) -| | +--- org.springframework.boot:spring-boot-starter-validation:4.0.0 -> 4.0.1 (*) -| | \--- io.projectreactor.addons:reactor-extra:3.6.0 (*) -| \--- org.springframework.boot:spring-boot-starter-webflux:4.0.0 -> 4.0.1 (*) -+--- org.springframework.cloud:spring-cloud-starter-consul-discovery -> 5.0.0 (*) -+--- org.springframework.boot:spring-boot-starter-actuator -> 4.0.1 -| +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| +--- org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.1 -| | +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| | \--- org.springframework.boot:spring-boot-micrometer-metrics:4.0.1 -| | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | +--- org.springframework.boot:spring-boot-micrometer-observation:4.0.1 -| | | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | | \--- io.micrometer:micrometer-observation:1.16.1 (*) -| | \--- io.micrometer:micrometer-core:1.16.1 -| | +--- org.jspecify:jspecify:1.0.0 -| | +--- io.micrometer:micrometer-commons:1.16.1 (*) -| | \--- io.micrometer:micrometer-observation:1.16.1 (*) -| +--- org.springframework.boot:spring-boot-actuator-autoconfigure:4.0.1 -| | +--- org.springframework.boot:spring-boot-autoconfigure:4.0.1 (*) -| | \--- org.springframework.boot:spring-boot-actuator:4.0.1 -| | \--- org.springframework.boot:spring-boot:4.0.1 (*) -| +--- org.springframework.boot:spring-boot-health:4.0.1 -| | \--- org.springframework.boot:spring-boot:4.0.1 (*) -| +--- io.micrometer:micrometer-observation:1.16.1 (*) -| \--- io.micrometer:micrometer-jakarta9:1.16.1 -| +--- org.jspecify:jspecify:1.0.0 -| +--- io.micrometer:micrometer-core:1.16.1 (*) -| +--- io.micrometer:micrometer-commons:1.16.1 (*) -| \--- io.micrometer:micrometer-observation:1.16.1 (*) -+--- org.springframework.boot:spring-boot-starter-security -> 4.0.1 -| +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| +--- org.springframework.boot:spring-boot-security:4.0.1 -| | +--- org.springframework.boot:spring-boot:4.0.1 (*) -| | +--- org.springframework.security:spring-security-config:7.0.2 -| | | +--- org.springframework.security:spring-security-core:7.0.2 -| | | | +--- org.springframework.security:spring-security-crypto:7.0.2 -| | | | +--- org.springframework:spring-aop:7.0.2 (*) -| | | | +--- org.springframework:spring-beans:7.0.2 (*) -| | | | +--- org.springframework:spring-context:7.0.2 (*) -| | | | +--- org.springframework:spring-core:7.0.2 (*) -| | | | +--- org.springframework:spring-expression:7.0.2 (*) -| | | | \--- io.micrometer:micrometer-observation:1.16.1 (*) -| | | +--- org.springframework:spring-aop:7.0.2 (*) -| | | +--- org.springframework:spring-beans:7.0.2 (*) -| | | +--- org.springframework:spring-context:7.0.2 (*) -| | | \--- org.springframework:spring-core:7.0.2 (*) -| | \--- org.springframework.security:spring-security-web:7.0.2 -| | +--- org.springframework.security:spring-security-core:7.0.2 (*) -| | +--- org.springframework:spring-core:7.0.2 (*) -| | +--- org.springframework:spring-aop:7.0.2 (*) -| | +--- org.springframework:spring-beans:7.0.2 (*) -| | +--- org.springframework:spring-context:7.0.2 (*) -| | +--- org.springframework:spring-expression:7.0.2 (*) -| | \--- org.springframework:spring-web:7.0.2 (*) -| \--- org.springframework:spring-aop:7.0.2 (*) -+--- org.springframework.boot:spring-boot-starter-oauth2-resource-server -> 4.0.1 -| +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) -| +--- org.springframework.boot:spring-boot-starter-security:4.0.1 (*) -| \--- org.springframework.boot:spring-boot-security-oauth2-resource-server:4.0.1 -| +--- org.springframework.boot:spring-boot:4.0.1 (*) -| +--- org.springframework.security:spring-security-oauth2-jose:7.0.2 -| | +--- org.springframework.security:spring-security-core:7.0.2 (*) -| | +--- org.springframework.security:spring-security-oauth2-core:7.0.2 -| | | +--- org.springframework.security:spring-security-core:7.0.2 (*) -| | | +--- org.springframework:spring-core:7.0.2 (*) -| | | \--- org.springframework:spring-web:7.0.2 (*) -| | +--- org.springframework:spring-core:7.0.2 (*) -| | \--- com.nimbusds:nimbus-jose-jwt:10.4 -| \--- org.springframework.security:spring-security-oauth2-resource-server:7.0.2 -| +--- org.springframework.security:spring-security-core:7.0.2 (*) -| +--- org.springframework.security:spring-security-oauth2-core:7.0.2 (*) -| +--- org.springframework.security:spring-security-web:7.0.2 (*) -| \--- org.springframework:spring-core:7.0.2 (*) -+--- org.springframework.security:spring-security-oauth2-jose -> 7.0.2 (*) -+--- org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j -> 5.0.0 -| +--- org.springframework.cloud:spring-cloud-starter:5.0.0 (*) -| +--- org.springframework.cloud:spring-cloud-circuitbreaker-resilience4j:5.0.0 -| | +--- com.fasterxml.jackson.core:jackson-core:2.20.1 (*) -| | +--- com.fasterxml.jackson.core:jackson-databind:2.20.1 (*) -| | +--- org.springframework.cloud:spring-cloud-commons:5.0.0 (*) -| | +--- io.micrometer:micrometer-observation:1.16.0 -> 1.16.1 (*) -| | +--- io.github.resilience4j:resilience4j-circuitbreaker:2.3.0 -| | | \--- io.github.resilience4j:resilience4j-core:2.3.0 -| | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 -> 2.3.0 -| | | +--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (*) -| | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.3.0 -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.3.0 (*) -| | +--- io.github.resilience4j:resilience4j-timelimiter:2.3.0 -| | | \--- io.github.resilience4j:resilience4j-core:2.3.0 (*) -| | +--- io.github.resilience4j:resilience4j-spring-boot3:2.3.0 -| | | \--- io.github.resilience4j:resilience4j-spring6:2.3.0 -| | | +--- io.github.resilience4j:resilience4j-annotations:2.3.0 -| | | +--- io.github.resilience4j:resilience4j-consumer:2.3.0 -| | | \--- io.github.resilience4j:resilience4j-framework-common:2.3.0 -| | | +--- io.github.resilience4j:resilience4j-circuitbreaker:2.3.0 (*) -| | | +--- io.github.resilience4j:resilience4j-ratelimiter:2.3.0 -| | | | \--- io.github.resilience4j:resilience4j-core:2.3.0 (*) -| | | +--- io.github.resilience4j:resilience4j-retry:2.3.0 -| | | | \--- io.github.resilience4j:resilience4j-core:2.3.0 (*) -| | | +--- io.github.resilience4j:resilience4j-timelimiter:2.3.0 (*) -| | | \--- io.github.resilience4j:resilience4j-micrometer:2.3.0 -| | | +--- io.micrometer:micrometer-core:1.10.0 -> 1.16.1 (*) -| | | \--- io.micrometer:micrometer-observation:1.10.0 -> 1.16.1 (*) -| | \--- org.springframework.boot:spring-boot-configuration-processor:4.0.0 -> 4.0.1 -| +--- io.github.resilience4j:resilience4j-circuitbreaker:2.3.0 (*) -| \--- io.github.resilience4j:resilience4j-timelimiter:2.3.0 (*) -+--- io.github.oshai:kotlin-logging-jvm:7.0.13 (*) -+--- ch.qos.logback:logback-classic:1.5.22 (*) -+--- ch.qos.logback:logback-core:1.5.22 -+--- com.fasterxml.jackson.module:jackson-module-kotlin -> 2.20.1 (*) -+--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310 -> 2.20.1 (*) -\--- org.springframework.boot:spring-boot-starter-data-redis -> 4.0.1 - +--- org.springframework.boot:spring-boot-starter:4.0.1 (*) - \--- org.springframework.boot:spring-boot-data-redis:4.0.1 - +--- org.springframework.boot:spring-boot:4.0.1 (*) - +--- org.springframework.boot:spring-boot-data-commons:4.0.1 - | +--- org.springframework.boot:spring-boot:4.0.1 (*) - | +--- org.springframework.boot:spring-boot-persistence:4.0.1 - | | +--- org.springframework.boot:spring-boot:4.0.1 (*) - | | \--- org.springframework:spring-tx:7.0.2 - | | +--- org.springframework:spring-beans:7.0.2 (*) - | | \--- org.springframework:spring-core:7.0.2 (*) - | \--- org.springframework.data:spring-data-commons:4.0.1 - | +--- org.springframework:spring-core:7.0.2 (*) - | +--- org.springframework:spring-beans:7.0.2 (*) - | \--- org.slf4j:slf4j-api:2.0.17 - +--- org.springframework.boot:spring-boot-transaction:4.0.1 - | +--- org.springframework.boot:spring-boot:4.0.1 (*) - | +--- org.springframework.boot:spring-boot-persistence:4.0.1 (*) - | \--- org.springframework:spring-tx:7.0.2 (*) - +--- io.lettuce:lettuce-core:6.8.1.RELEASE -> 7.2.1.RELEASE - | +--- redis.clients.authentication:redis-authx-core:0.1.1-beta2 - | | \--- org.slf4j:slf4j-api:1.7.36 -> 2.0.17 - | +--- io.netty:netty-common:4.2.5.Final -> 4.2.9.Final - | +--- io.netty:netty-handler:4.2.5.Final -> 4.2.9.Final (*) - | +--- io.netty:netty-transport:4.2.5.Final -> 4.2.9.Final (*) - | +--- io.projectreactor:reactor-core:3.6.6 -> 3.8.1 (*) - | \--- io.netty:netty-resolver-dns:4.2.5.Final -> 4.2.9.Final (*) - \--- org.springframework.data:spring-data-redis:4.0.1 - +--- org.springframework.data:spring-data-keyvalue:4.0.1 - | +--- org.springframework.data:spring-data-commons:4.0.1 (*) - | +--- org.springframework:spring-context:7.0.2 (*) - | +--- org.springframework:spring-tx:7.0.2 (*) - | \--- org.slf4j:slf4j-api:2.0.17 - +--- org.springframework:spring-tx:7.0.2 (*) - +--- org.springframework:spring-oxm:7.0.2 - | +--- org.springframework:spring-beans:7.0.2 (*) - | \--- org.springframework:spring-core:7.0.2 (*) - +--- org.springframework:spring-aop:7.0.2 (*) - +--- org.springframework:spring-context-support:7.0.2 (*) - \--- org.slf4j:slf4j-api:2.0.17 - -(c) - A dependency constraint, not a dependency. The dependency affected by the constraint occurs elsewhere in the tree. -(*) - Indicates repeated occurrences of a transitive dependency subtree. Gradle expands transitive dependency subtrees only once per project; repeat occurrences only display the root of the subtree, followed by this annotation. - -A web-based, searchable dependency report is available by adding the --scan option. - -[Incubating] Problems report is available at: file:///home/stefan-mo/WsMeldestelle/Meldestelle/build/reports/problems/problems-report.html - -Deprecated Gradle features were used in this build, making it incompatible with Gradle 10. - -You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins. - -For more on this, please refer to https://docs.gradle.org/9.2.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation. - -BUILD SUCCESSFUL in 3s -1 actionable task: 1 executed diff --git a/GeminiBerichte/Tech-Stack_Referenz_Kotlin_2-3-0_Java-25_KMP.md b/GeminiBerichte/Tech-Stack_Referenz_Kotlin_2-3-0_Java-25_KMP.md index 99c445ff..27b3c643 100644 --- a/GeminiBerichte/Tech-Stack_Referenz_Kotlin_2-3-0_Java-25_KMP.md +++ b/GeminiBerichte/Tech-Stack_Referenz_Kotlin_2-3-0_Java-25_KMP.md @@ -3,11 +3,11 @@ ### 1. Kern-Spezifikationen -| Komponente | Version | Status | -| --- | --- | --- | -| **Kotlin** | `2.3.0` | Stabil (K2 Compiler standardmäßig aktiv) | -| **Java (JDK)** | `25` | LTS (Long-Term Support) | -| **Gradle** | `9.2+` | Erforderlich für JDK 25 Support | +| Komponente | Version | Status | +| --- |----------| --- | +| **Kotlin** | `2.3.0` | Stabil (K2 Compiler standardmäßig aktiv) | +| **Java (JDK)** | `25` | LTS (Long-Term Support) | +| **Gradle** | `9.2.1` | Erforderlich für JDK 25 Support | | **Android Plugin (AGP)** | `8.8.0+` | Empfohlen für Gradle 9.x Kompatibilität | --- @@ -52,7 +52,7 @@ Damit das Projekt Java 25 erkennt, muss der Wrapper auf dem neuesten Stand sein: **Terminal-Befehl:** ```bash -./gradlew wrapper --gradle-version 9.2 --distribution-type all +./gradlew wrapper --gradle-version 9.2.1 --distribution-type all ``` diff --git a/backend/infrastructure/gateway/build.gradle.kts b/backend/infrastructure/gateway/build.gradle.kts index 8a3f8d80..3e9db651 100644 --- a/backend/infrastructure/gateway/build.gradle.kts +++ b/backend/infrastructure/gateway/build.gradle.kts @@ -25,7 +25,7 @@ dependencies { // === GATEWAY-SPEZIFISCHE ABHÄNGIGKEITEN === // Die WebFlux-Abhängigkeit wird jetzt korrekt durch das BOM bereitgestellt. - // implementation(libs.spring.boot.starter.webflux) + implementation(libs.spring.boot.starter.webflux) // Kern-Gateway inkl. Security, Actuator, CircuitBreaker, Discovery implementation(libs.bundles.gateway.core) @@ -34,6 +34,12 @@ dependencies { // Redis-Unterstützung für verteiltes Rate Limiting (RequestRateLimiter) implementation(libs.bundles.gateway.redis) + // === Tracing Dependencies (Micrometer Tracing) === + // Ermöglicht verteiltes Tracing über Thread-Grenzen hinweg (ersetzt manuellen MDC-Filter) + implementation(libs.micrometer.tracing.bridge.brave) + // Optional: Zipkin Reporter, falls du Traces an Zipkin senden willst (bereits im monitoringClient enthalten, aber hier explizit schadet nicht) + // implementation(libs.zipkin.reporter.brave) + // === Test Dependencies === testImplementation(projects.platform.platformTesting) testImplementation(libs.bundles.testing.jvm) diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt index d3bfbe2a..38637b95 100644 --- a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt +++ b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/GatewayApplication.kt @@ -1,6 +1,7 @@ package at.mocode.infrastructure.gateway import org.slf4j.LoggerFactory +import org.springframework.beans.factory.getBean import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.core.env.Environment @@ -11,7 +12,7 @@ class GatewayApplication fun main(args: Array) { val context = runApplication(*args) val logger = LoggerFactory.getLogger(GatewayApplication::class.java) - val env = context.getBean(Environment::class.java) + val env = context.getBean() val port = env.getProperty("server.port") ?: "8081" logger.info(""" diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt index 38844257..de3cc527 100644 --- a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt +++ b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/GatewayConfig.kt @@ -1,5 +1,6 @@ package at.mocode.infrastructure.gateway.config +import io.micrometer.tracing.Tracer import org.slf4j.LoggerFactory import org.springframework.cloud.gateway.filter.GatewayFilterChain import org.springframework.cloud.gateway.filter.GlobalFilter @@ -7,17 +8,21 @@ import org.springframework.core.Ordered import org.springframework.stereotype.Component import org.springframework.web.server.ServerWebExchange import reactor.core.publisher.Mono -import java.util.* /** * Gateway-Konfiguration für erweiterte Funktionalitäten wie Logging, Rate Limiting und Security. */ /** - * Global Filter für Correlations-IDs zur Request-Verfolgung. + * Globaler Filter, der sicherstellt, dass die Trace-ID (von Micrometer Tracing) + * auch als "X-Correlation-ID" im Response-Header zurückgegeben wird. + * + * Hinweis: Micrometer Tracing kümmert sich bereits automatisch um die Propagation + * der Trace-ID (b3 oder w3c) an nachgelagerte Services. Dieser Filter dient nur + * der Bequemlichkeit für Clients (z. B. Frontend), um die ID einfach auslesen zu können. */ @Component -class CorrelationIdFilter : GlobalFilter, Ordered { +class CorrelationIdFilter(private val tracer: Tracer) : GlobalFilter, Ordered { private val logger = LoggerFactory.getLogger(CorrelationIdFilter::class.java) @@ -26,26 +31,21 @@ class CorrelationIdFilter : GlobalFilter, Ordered { } override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono { - val request = exchange.request - val correlationId = request.headers.getFirst(CORRELATION_ID_HEADER) - ?: UUID.randomUUID().toString() + // Die aktuelle Trace-ID aus dem Micrometer Tracer holen + val currentSpan = tracer.currentSpan() + val traceId = currentSpan?.context()?.traceId() - val mutatedRequest = request.mutate() - .header(CORRELATION_ID_HEADER, correlationId) - .build() + if (traceId != null) { + // Trace-ID als Response-Header hinzufügen + exchange.response.headers.add(CORRELATION_ID_HEADER, traceId) + } - val mutatedExchange = exchange.mutate() - .request(mutatedRequest) - .build() - - // Response-Header nach der Verarbeitung hinzufügen - mutatedExchange.response.headers.add(CORRELATION_ID_HEADER, correlationId) - - return chain.filter(mutatedExchange) + return chain.filter(exchange) .doOnError { ex -> - logger.error("Error in CorrelationIdFilter for request {}: {}", request.uri, ex.message) + logger.error("Error processing request {}: {}", exchange.request.uri, ex.message) } } - override fun getOrder(): Int = Ordered.HIGHEST_PRECEDENCE + // Niedrige Priorität, damit Tracing-Kontext bereits initialisiert ist + override fun getOrder(): Int = Ordered.LOWEST_PRECEDENCE } diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimitConfig.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimitConfig.kt new file mode 100644 index 00000000..ae59512e --- /dev/null +++ b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimitConfig.kt @@ -0,0 +1,51 @@ +package at.mocode.infrastructure.gateway.config + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary +import reactor.core.publisher.Mono +import java.security.Principal + +@Configuration +class RateLimitConfig { + + /** + * Standard KeyResolver: IP-basiert. + * Nutzt X-Forwarded-For (für Proxies/LoadBalancer), wenn vorhanden, sonst die Remote-Adresse. + * Wird verwendet, wenn kein anderer KeyResolver explizit angefordert wird oder aktiv ist. + */ + @Bean + @Primary + fun ipAddressKeyResolver(): KeyResolver = KeyResolver { exchange -> + val forwardedFor = exchange.request.headers.getFirst("X-Forwarded-For") + ?.split(',')?.firstOrNull()?.trim() + val ip = forwardedFor + ?: exchange.request.remoteAddress?.address?.hostAddress + ?: "unknown" + Mono.just(ip) + } + + /** + * Erweiterter KeyResolver: Principal-basiert (User-ID). + * Versucht, den authentifizierten User (Principal) zu nutzen. + * Fallback auf IP-Adresse, falls der User nicht eingeloggt ist. + * + * Aktivierung über Property: gateway.ratelimit.principal-key-resolver.enabled=true + */ + @Bean + @ConditionalOnProperty(prefix = "gateway.ratelimit.principal-key-resolver", name = ["enabled"], havingValue = "true", matchIfMissing = false) + fun principalNameKeyResolver(): KeyResolver = KeyResolver { exchange -> + exchange.getPrincipal() + .map { it.name } + .switchIfEmpty( + Mono.just( + exchange.request.headers.getFirst("X-Forwarded-For")?.split(",")?.first()?.trim() + ?: exchange.request.headers.getFirst("X-Real-IP") + ?: exchange.request.remoteAddress?.address?.hostAddress + ?: "unknown" + ) + ) + } +} diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimiterConfig.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimiterConfig.kt deleted file mode 100644 index 51240a55..00000000 --- a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimiterConfig.kt +++ /dev/null @@ -1,31 +0,0 @@ -package at.mocode.infrastructure.gateway.config - -import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import reactor.core.publisher.Mono -import java.security.Principal - -@Configuration -class RateLimiterConfig { - - /** - * KeyResolver basierend auf authentifiziertem Principal; Fallback auf Client-IP. - * Funktioniert out-of-the-box mit Keycloak (Resource Server), sofern Security aktiv ist. - */ - @Bean - @ConditionalOnProperty(prefix = "gateway.ratelimit.principal-key-resolver", name = ["enabled"], havingValue = "true", matchIfMissing = false) - fun principalNameKeyResolver(): KeyResolver = KeyResolver { exchange -> - exchange.getPrincipal() - .map { it.name } - .switchIfEmpty( - Mono.just( - exchange.request.headers.getFirst("X-Forwarded-For")?.split(",")?.first()?.trim() - ?: exchange.request.headers.getFirst("X-Real-IP") - ?: exchange.request.remoteAddress?.address?.hostAddress - ?: "unknown" - ) - ) - } -} diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimitingConfig.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimitingConfig.kt deleted file mode 100644 index e66235be..00000000 --- a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/config/RateLimitingConfig.kt +++ /dev/null @@ -1,26 +0,0 @@ -package at.mocode.infrastructure.gateway.config - -import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver -import org.springframework.context.annotation.Primary -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import reactor.core.publisher.Mono - -@Configuration -class RateLimitingConfig { - - /** - * Einfache IP-basierte KeyResolver-Implementierung für das RequestRateLimiter-Filter. - * Nutzt X-Forwarded-For, wenn vorhanden, sonst die Remote-Adresse. - */ - @Bean - @Primary - fun ipAddressKeyResolver(): KeyResolver = KeyResolver { exchange -> - val forwardedFor = exchange.request.headers.getFirst("X-Forwarded-For") - ?.split(',')?.firstOrNull()?.trim() - val ip = forwardedFor - ?: exchange.request.remoteAddress?.address?.hostAddress - ?: "unknown" - Mono.just(ip) - } -} diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/fallback/FallbackController.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/fallback/FallbackController.kt deleted file mode 100644 index 3c819bb9..00000000 --- a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/fallback/FallbackController.kt +++ /dev/null @@ -1,27 +0,0 @@ -package at.mocode.infrastructure.gateway.fallback - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController -import java.time.Instant - -/** - * Alternative FallbackController (deaktiviert per Default), nur aktivierbar über - * property `gateway.customFallback.enabled=true`. Standardmäßig existiert bereits - * ein FallbackController unter `...gateway.controller.FallbackController`. - */ -@RestController -@ConditionalOnProperty(prefix = "gateway.customFallback", name = ["enabled"], havingValue = "true", matchIfMissing = false) -class FallbackController { - - @RequestMapping("/fallback/ping") - fun pingFallback(): ResponseEntity> = - ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body( - mapOf( - "message" to "Ping service unavailable", - "timestamp" to Instant.now().toString() - ) - ) -} diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt index 8ce6c554..a6fb042a 100644 --- a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt +++ b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/health/GatewayHealthIndicator.kt @@ -1,7 +1,8 @@ package at.mocode.infrastructure.gateway.health -import org.springframework.boot.actuate.health.Health -import org.springframework.boot.actuate.health.ReactiveHealthIndicator +import org.springframework.boot.health.contributor.Health +import org.springframework.boot.health.contributor.ReactiveHealthIndicator +import org.springframework.cloud.client.ServiceInstance import org.springframework.cloud.client.discovery.DiscoveryClient import org.springframework.core.env.Environment import org.springframework.stereotype.Component @@ -9,7 +10,9 @@ import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClientResponseException import reactor.core.publisher.Flux import reactor.core.publisher.Mono +import reactor.core.scheduler.Schedulers import java.time.Duration +import java.util.concurrent.TimeoutException /** * Gateway Health Indicator zur Überwachung der Downstream Services. @@ -40,46 +43,47 @@ class GatewayHealthIndicator( ) private val HEALTH_CHECK_TIMEOUT = Duration.ofSeconds(5) + private const val PARALLELISM = 8 } - // KORREKTUR für Spring Boot 4: Die `health()`-Methode und ihr Rückgabetyp `Mono` - // erlauben keine Null-Werte mehr. Die Fragezeichen (?) wurden entfernt. override fun health(): Mono { val builder = Health.up() val details = mutableMapOf() return Mono.fromCallable { discoveryClient.services } - .flatMapMany { services -> + .subscribeOn(Schedulers.boundedElastic()) + .flatMapMany { services: List -> details["totalServices"] = services.size Flux.fromIterable(services) } - .flatMap({ serviceName -> - val instances = discoveryClient.getInstances(serviceName) - val instanceDetails = mapOf( - "instanceCount" to instances.size, - "instances" to instances.map { "${it.host}:${it.port}" } - ) - // Für Health-Check nur auf definierte Services gehen - val checkMono = when { - CRITICAL_SERVICES.contains(serviceName) || OPTIONAL_SERVICES.contains(serviceName) -> - checkServiceHealthReactive(serviceName) - else -> Mono.just("SKIPPED") - } - checkMono - .map { status -> Triple(serviceName, status, instanceDetails) } - }, 8) // begrenze Parallelität + .flatMap({ serviceName: String -> + Mono.fromCallable { discoveryClient.getInstances(serviceName) } + .subscribeOn(Schedulers.boundedElastic()) + .flatMap { instances: List -> + val instanceDetails = mapOf( + "instanceCount" to instances.size, + "instances" to instances.map { "${it.host}:${it.port}" } + ) + + val checkMono: Mono = when { + CRITICAL_SERVICES.contains(serviceName) || OPTIONAL_SERVICES.contains(serviceName) -> + checkServiceHealthReactive(serviceName, instances) + else -> Mono.just("SKIPPED") + } + checkMono.map { status -> Triple(serviceName, status, instanceDetails) } + } + }, PARALLELISM) .collectList() - .map { results -> + .flatMap { results: List>> -> val discoveredServices = mutableMapOf() val criticalServiceStatus = mutableMapOf() val optionalServiceStatus = mutableMapOf() results.forEach { (serviceName, status, instanceDetails) -> discoveredServices[serviceName] = instanceDetails - if (CRITICAL_SERVICES.contains(serviceName)) { - criticalServiceStatus[serviceName] = status - } else if (OPTIONAL_SERVICES.contains(serviceName)) { - optionalServiceStatus[serviceName] = status + when { + CRITICAL_SERVICES.contains(serviceName) -> criticalServiceStatus[serviceName] = status + OPTIONAL_SERVICES.contains(serviceName) -> optionalServiceStatus[serviceName] = status } } @@ -99,48 +103,48 @@ class GatewayHealthIndicator( details["status"] = "UP" details["reason"] = when { isTestEnvironment -> "Gesundheitsprüfung erfolgreich (Testumgebung)" - isDevEnvironment -> "Gesundheitsprüfung erfolgreich (Entwicklungsumgebung - nicht alle Services erforderlich)" - else -> "Alle kritischen Services sind verfügbar oder optional" + isDevEnvironment -> "Gesundheitsprüfung erfolgreich (Entwicklungsumgebung)" + else -> "Alle kritischen Services sind verfügbar" } } - builder.withDetails(details).build() + Mono.just(builder.withDetails(details).build()) } .onErrorResume { ex -> Mono.just( Health.down(ex) .withDetail("status", "DOWN") - .withDetail("reason", "Fehler beim Prüfen der nachgelagerten Services: ${ex.message}") + .withDetail("reason", "Fehler bei der Service-Prüfung: ${ex.message}") .build() ) } } - private fun checkServiceHealthReactive(serviceName: String): Mono { - return Mono.fromCallable { discoveryClient.getInstances(serviceName) } - .flatMap { instances -> - if (instances.isEmpty()) { - Mono.just("NO_INSTANCES") - } else { - val instance = instances.first() - val healthUrl = "http://${instance.host}:${instance.port}/actuator/health" - webClient.get() - .uri(healthUrl) - .retrieve() - .bodyToMono(Map::class.java) - .timeout(HEALTH_CHECK_TIMEOUT) - .map { it["status"]?.toString() ?: "UNKNOWN" } - .map { status -> if (status == "UP") "UP" else "DOWN" } - .onErrorResume { ex -> - when (ex) { - is WebClientResponseException -> when (ex.statusCode.value()) { - 404 -> Mono.just("NO_HEALTH_ENDPOINT") - 503 -> Mono.just("DOWN") - else -> Mono.just("ERROR") - } - else -> Mono.just("ERROR") - } - } + private fun checkServiceHealthReactive(serviceName: String, instances: List): Mono { + if (instances.isEmpty()) { + return Mono.just("NO_INSTANCES") + } + + // Wir prüfen exemplarisch die erste Instanz + val instance = instances.first() + val healthUrl = "http://${instance.host}:${instance.port}/actuator/health" + + return webClient.get() + .uri(healthUrl) + .retrieve() + .bodyToMono(Map::class.java) + .timeout(HEALTH_CHECK_TIMEOUT) + .map { it["status"]?.toString() ?: "UNKNOWN" } + .map { status -> if (status.equals("UP", ignoreCase = true)) "UP" else "DOWN" } + .onErrorResume { ex -> + when (ex) { + is WebClientResponseException -> when (ex.statusCode.value()) { + 404 -> Mono.just("NO_HEALTH_ENDPOINT") + 503 -> Mono.just("DOWN") + else -> Mono.just("ERROR") + } + is TimeoutException -> Mono.just("TIMEOUT") + else -> Mono.just("ERROR") } } } diff --git a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt index b98d5a9a..8e6663c8 100644 --- a/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt +++ b/backend/infrastructure/gateway/src/main/kotlin/at/mocode/infrastructure/gateway/security/SecurityConfig.kt @@ -16,6 +16,7 @@ import org.springframework.security.web.server.util.matcher.ServerWebExchangeMat import org.springframework.web.cors.CorsConfiguration import org.springframework.web.cors.reactive.CorsConfigurationSource import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource +import reactor.core.publisher.Mono import java.time.Duration @Configuration @@ -69,35 +70,53 @@ class SecurityConfig( * Erstellt einen ReactiveJwtDecoder für die JWT-Validierung. * * Verwendet die JWK Set URI aus der Konfiguration, um die öffentlichen Schlüssel - * von Keycloak zu laden. Falls die URI nicht konfiguriert ist oder Keycloak - * nicht erreichbar ist, wird trotzdem ein Bean erstellt, um Startfehler zu vermeiden. + * von Keycloak zu laden. + * + * Resilience-Optimierung: + * Anstatt beim Start zu failen oder einen statischen NoOp-Decoder zu nutzen, + * verwenden wir einen delegierenden Decoder. Dieser versucht bei jedem Request, + * den echten Decoder (lazy) zu initialisieren, falls er noch nicht bereit ist. + * So kann Keycloak auch NACH dem Gateway starten. */ @Bean fun reactiveJwtDecoder( @Value($$"${spring.security.oauth2.resourceserver.jwt.jwk-set-uri:}") jwkSetUri: String ): ReactiveJwtDecoder { - return if (jwkSetUri.isNotBlank()) { - try { - NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build() - } catch (e: Exception) { - // Log warning and return a no-op decoder to allow startup - logger.warn("Failed to configure JWT decoder with JWK Set URI: {} - {}", jwkSetUri, e.message) - logger.warn("JWT authentication will not work until Keycloak is available") - createNoOpJwtDecoder() - } - } else { - logger.info("No JWK Set URI configured, using no-op JWT decoder") - createNoOpJwtDecoder() - } + return ResilienceReactiveJwtDecoder(jwkSetUri) } /** - * Erstellt einen No-Op JWT Decoder für Fälle, in denen Keycloak nicht verfügbar ist. - * Dieser Decoder lehnt alle Token ab, erlaubt aber den Anwendungsstart. + * Ein Wrapper um den NimbusReactiveJwtDecoder, der Initialisierungsfehler abfängt + * und erst zur Laufzeit (lazy) versucht, die JWKs zu laden. */ - private fun createNoOpJwtDecoder(): ReactiveJwtDecoder { - return ReactiveJwtDecoder { token -> - throw IllegalStateException("JWT validation is not available - Keycloak may not be running") + class ResilienceReactiveJwtDecoder(private val jwkSetUri: String) : ReactiveJwtDecoder { + private val logger = LoggerFactory.getLogger(ResilienceReactiveJwtDecoder::class.java) + private var delegate: ReactiveJwtDecoder? = null + + override fun decode(token: String): Mono { + if (delegate == null) { + synchronized(this) { + if (delegate == null) { + try { + if (jwkSetUri.isBlank()) { + throw IllegalArgumentException("JWK Set URI is missing") + } + logger.info("Attempting to initialize JWT Decoder with URI: {}", jwkSetUri) + delegate = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build() + logger.info("JWT Decoder successfully initialized.") + } catch (e: Exception) { + logger.warn("Could not initialize JWT Decoder (Keycloak might be down): {}", e.message) + return Mono.error(IllegalStateException("Identity Provider currently unavailable. Please try again later.")) + } + } + } + } + return delegate!!.decode(token) + .onErrorResume { e -> + // Falls der Decoder zwar da ist, aber z.B. Netzwerkfehler auftreten, loggen wir das + logger.debug("JWT decoding failed: {}", e.message) + Mono.error(e) + } } } diff --git a/backend/infrastructure/gateway/src/main/resources/application.yaml b/backend/infrastructure/gateway/src/main/resources/application.yaml index 9dd51adb..27dd504d 100644 --- a/backend/infrastructure/gateway/src/main/resources/application.yaml +++ b/backend/infrastructure/gateway/src/main/resources/application.yaml @@ -27,6 +27,11 @@ management: web: exposure: include: health,info,prometheus + tracing: + sampling: + probability: 1.0 # 100% der Requests tracen (für Dev/Test sinnvoll, in Prod reduzieren) + propagation: + type: w3c # Standard W3C Trace Context (kompatibel mit OpenTelemetry) # Gateway-spezifische Einstellungen gateway: diff --git a/backend/services/ping/ping-service/build.gradle.kts b/backend/services/ping/ping-service/build.gradle.kts index d1e1465c..5cf8793a 100644 --- a/backend/services/ping/ping-service/build.gradle.kts +++ b/backend/services/ping/ping-service/build.gradle.kts @@ -1,61 +1,38 @@ -// Optimized Spring Boot ping service for testing microservice architecture -// This service demonstrates circuit breaker patterns, service discovery, and monitoring plugins { - alias(libs.plugins.kotlinJvm) - alias(libs.plugins.kotlinSpring) - alias(libs.plugins.kotlinJpa) - alias(libs.plugins.spring.boot) - // FINALE BEREINIGUNG: Das `dependencyManagement`-Plugin wird entfernt. - // alias(libs.plugins.spring.dependencyManagement) + alias(libs.plugins.kotlinJvm) + alias(libs.plugins.kotlinSpring) + alias(libs.plugins.kotlinJpa) + alias(libs.plugins.spring.boot) } -// Configure the main class for the executable JAR -springBoot { - mainClass.set("at.mocode.ping.service.PingServiceApplicationKt") +kotlin { + compilerOptions { + // Aktiviert die experimentelle UUID API von Kotlin 2.3.0 + freeCompilerArgs.add("-opt-in=kotlin.uuid.ExperimentalUuidApi") + } } dependencies { - // Die `platform`-Deklaration ist der einzig korrekte Weg. - implementation(platform(projects.platform.platformBom)) + // === Project Dependencies === + implementation(projects.backend.services.ping.pingApi) + implementation(projects.platform.platformDependencies) - // Platform und Core Dependencies - implementation(projects.platform.platformDependencies) - implementation(projects.backend.services.ping.pingApi) - implementation(projects.backend.infrastructure.monitoring.monitoringClient) + // === Spring Boot & Cloud === + implementation(libs.bundles.spring.boot.service.complete) + // WICHTIG: Da wir JPA (blockierend) nutzen, brauchen wir Spring MVC (nicht WebFlux) + implementation(libs.spring.boot.starter.web) + implementation(libs.bundles.spring.cloud.gateway) // Für Discovery Client - // Spring Boot Service Complete Bundle - // Provides: web, validation, actuator, security, oauth2-client, oauth2-resource-server, - // data-jpa, data-redis, micrometer-prometheus, tracing, zipkin - implementation(libs.bundles.spring.boot.service.complete) + // === Database & Persistence === + implementation(libs.bundles.database.complete) - // Datenbank (PostgresQL) Driver - implementation(libs.postgresql.driver) + // === Resilience === + implementation(libs.bundles.resilience) - // Web-Server (Tomcat) explizit hinzufügen! - implementation(libs.spring.boot.starter.web) - - // Jackson Kotlin Support Bundle - implementation(libs.bundles.jackson.kotlin) - - // Kotlin Reflection (now from version catalog) - implementation(libs.kotlin.reflect) - - // Service Discovery - implementation(libs.spring.cloud.starter.consul.discovery) - - // Caching (Caffeine for Spring Cloud LoadBalancer) - implementation(libs.caffeine) - implementation(libs.spring.web) // Provides spring-context-support - - // Resilience4j Bundle (Circuit Breaker, Reactor, AOP) - implementation(libs.bundles.resilience) - - // OpenAPI Documentation - implementation(libs.springdoc.openapi.starter.webmvc.ui) - - // Test Dependencies - testImplementation(projects.platform.platformTesting) - testImplementation(libs.bundles.testing.jvm) - testImplementation(libs.spring.boot.starter.test) - testImplementation(libs.spring.boot.starter.web) + // === Testing === + testImplementation(libs.bundles.testing.jvm) +} + +tasks.test { + useJUnitPlatform() } diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceApplication.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/PingServiceApplication.kt similarity index 86% rename from backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceApplication.kt rename to backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/PingServiceApplication.kt index 0af4ae12..0bf8a86b 100644 --- a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceApplication.kt +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/PingServiceApplication.kt @@ -1,13 +1,13 @@ -package at.mocode.ping.service +package at.mocode.ping import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.EnableAspectJAutoProxy -import org.springframework.web.servlet.config.annotation.CorsRegistry -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer +import org.springframework.web.reactive.config.CorsRegistry @SpringBootApplication +// Scannt explizit alle Sub-Packages (infrastructure, application, domain) @EnableAspectJAutoProxy class PingServiceApplication { diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/application/PingService.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/application/PingService.kt new file mode 100644 index 00000000..dbacaaee --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/application/PingService.kt @@ -0,0 +1,44 @@ +package at.mocode.ping.application + +import at.mocode.ping.domain.Ping +import at.mocode.ping.domain.PingRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +/** + * Application Service. + * Implementiert den Use Case und orchestriert Domain & Repository. + * Hier darf Spring (@Service, @Transactional) verwendet werden, da es "Application Logic" ist. + */ +@Service +@OptIn(ExperimentalUuidApi::class) +class PingService( + private val repository: PingRepository +) : PingUseCase { + + private val logger = LoggerFactory.getLogger(PingService::class.java) + + @Transactional + override fun executePing(message: String): Ping { + logger.info("Executing ping with message: {}", message) + + // Domain Logic: Erstelle neue Entity (generiert UUID v7 automatisch) + val ping = Ping(message = message) + + // Persistence + return repository.save(ping) + } + + @Transactional(readOnly = true) + override fun getPingHistory(): List { + return repository.findAll() + } + + @Transactional(readOnly = true) + override fun getPing(id: Uuid): Ping? { + return repository.findById(id) + } +} diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/application/PingUseCase.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/application/PingUseCase.kt new file mode 100644 index 00000000..d205a862 --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/application/PingUseCase.kt @@ -0,0 +1,16 @@ +package at.mocode.ping.application + +import at.mocode.ping.domain.Ping +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +/** + * Primary Port (Inbound Port). + * Definiert die fachlichen Operationen, die von außen (Controller) aufgerufen werden können. + */ +@OptIn(ExperimentalUuidApi::class) +interface PingUseCase { + fun executePing(message: String): Ping + fun getPingHistory(): List + fun getPing(id: Uuid): Ping? +} diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/domain/Ping.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/domain/Ping.kt new file mode 100644 index 00000000..70062dd9 --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/domain/Ping.kt @@ -0,0 +1,16 @@ +package at.mocode.ping.domain + +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid +import java.time.Instant + +/** + * Domain Entity für einen Ping. + * Unabhängig von Frameworks (Pure Kotlin). + */ +@OptIn(ExperimentalUuidApi::class) +data class Ping( + val id: Uuid = Uuid.generateV7(), // Kotlin 2.3.0 UUID v7 + val message: String, + val timestamp: Instant = Instant.now() +) diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/domain/PingRepository.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/domain/PingRepository.kt new file mode 100644 index 00000000..9ba1b3d7 --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/domain/PingRepository.kt @@ -0,0 +1,15 @@ +package at.mocode.ping.domain + +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +/** + * Secondary Port (Outbound Port). + * Definiert, wie Pings gespeichert werden, ohne die Technologie (DB) zu kennen. + */ +@OptIn(ExperimentalUuidApi::class) +interface PingRepository { + fun save(ping: Ping): Ping + fun findAll(): List + fun findById(id: Uuid): Ping? +} diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/PingJpaEntity.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/PingJpaEntity.kt new file mode 100644 index 00000000..c135a711 --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/PingJpaEntity.kt @@ -0,0 +1,24 @@ +package at.mocode.ping.infrastructure.persistence + +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.time.Instant +import java.util.UUID + +/** + * JPA Entity (Infrastructure Detail). + * Spiegelt die Datenbank-Tabelle wider. + * Nutzt java.util.UUID für JPA-Kompatibilität (bis Hibernate kotlin.uuid nativ unterstützt). + */ +@Entity +@Table(name = "pings") +class PingJpaEntity( + @Id + val id: UUID, + val message: String, + val timestamp: Instant +) { + // Default constructor for JPA + protected constructor() : this(UUID.randomUUID(), "", Instant.now()) +} diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/PingRepositoryAdapter.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/PingRepositoryAdapter.kt new file mode 100644 index 00000000..85d851cf --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/PingRepositoryAdapter.kt @@ -0,0 +1,49 @@ +package at.mocode.ping.infrastructure.persistence + +import at.mocode.ping.domain.Ping +import at.mocode.ping.domain.PingRepository +import org.springframework.stereotype.Component +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid +import kotlin.uuid.toJavaUuid +import kotlin.uuid.toKotlinUuid + +/** + * Driven Adapter. + * Implementiert den Domain-Port `PingRepository` mithilfe von Spring Data JPA. + * Mappt zwischen Domain-Entity und JPA-Entity. + */ +@Component +@OptIn(ExperimentalUuidApi::class) +class PingRepositoryAdapter( + private val jpaRepository: SpringDataPingRepository +) : PingRepository { + + override fun save(ping: Ping): Ping { + val jpaEntity = PingJpaEntity( + id = ping.id.toJavaUuid(), + message = ping.message, + timestamp = ping.timestamp + ) + val saved = jpaRepository.save(jpaEntity) + return mapToDomain(saved) + } + + override fun findAll(): List { + return jpaRepository.findAll().map { mapToDomain(it) } + } + + override fun findById(id: Uuid): Ping? { + return jpaRepository.findById(id.toJavaUuid()) + .map { mapToDomain(it) } + .orElse(null) + } + + private fun mapToDomain(entity: PingJpaEntity): Ping { + return Ping( + id = entity.id.toKotlinUuid(), + message = entity.message, + timestamp = entity.timestamp + ) + } +} diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/SpringDataPingRepository.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/SpringDataPingRepository.kt new file mode 100644 index 00000000..8505f40a --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/persistence/SpringDataPingRepository.kt @@ -0,0 +1,8 @@ +package at.mocode.ping.infrastructure.persistence + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.util.UUID + +@Repository +interface SpringDataPingRepository : JpaRepository diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/web/PingController.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/web/PingController.kt new file mode 100644 index 00000000..261b1e32 --- /dev/null +++ b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/infrastructure/web/PingController.kt @@ -0,0 +1,96 @@ +package at.mocode.ping.infrastructure.web + +import at.mocode.ping.api.EnhancedPingResponse +import at.mocode.ping.api.HealthResponse +import at.mocode.ping.api.PingApi +import at.mocode.ping.api.PingResponse +import at.mocode.ping.application.PingUseCase +import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker +import org.slf4j.LoggerFactory +import org.springframework.web.bind.annotation.* +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import kotlin.random.Random + +/** + * Driving Adapter (REST Controller). + * Nutzt den Application Port (PingUseCase). + */ +@RestController +@CrossOrigin(allowedHeaders = ["*"], allowCredentials = "true") +class PingController( + private val pingUseCase: PingUseCase +) : PingApi { + + private val logger = LoggerFactory.getLogger(PingController::class.java) + private val formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME + + companion object { + const val PING_CIRCUIT_BREAKER = "pingCircuitBreaker" + } + + @GetMapping("/ping/simple") + override suspend fun simplePing(): PingResponse { + // Ruft Use Case auf -> Speichert in DB + val domainPing = pingUseCase.executePing("Simple Ping") + + return PingResponse( + status = "pong", + timestamp = domainPing.timestamp.atOffset(ZoneOffset.UTC).format(formatter), + service = "ping-service" + ) + } + + @GetMapping("/ping/enhanced") + @CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackPing") + override suspend fun enhancedPing( + @RequestParam(required = false, defaultValue = "false") simulate: Boolean + ): EnhancedPingResponse { + val start = System.nanoTime() + + if (simulate && Random.nextDouble() < 0.6) { + throw RuntimeException("Simulated service failure") + } + + // Use Case Aufruf + val domainPing = pingUseCase.executePing("Enhanced Ping") + + val elapsedMs = (System.nanoTime() - start) / 1_000_000 + + return EnhancedPingResponse( + status = "pong", + timestamp = domainPing.timestamp.atOffset(ZoneOffset.UTC).format(formatter), + service = "ping-service", + circuitBreakerState = "CLOSED", + responseTime = elapsedMs + ) + } + + // Fallback muss public sein für Resilience4j Proxy + fun fallbackPing(simulate: Boolean, ex: Exception): EnhancedPingResponse { + logger.warn("Circuit breaker fallback triggered: {}", ex.message) + return EnhancedPingResponse( + status = "fallback", + timestamp = java.time.OffsetDateTime.now().format(formatter), + service = "ping-service-fallback", + circuitBreakerState = "OPEN", + responseTime = 0 + ) + } + + @GetMapping("/ping/health") + override suspend fun healthCheck(): HealthResponse { + return HealthResponse( + status = "up", + timestamp = java.time.OffsetDateTime.now().format(formatter), + service = "ping-service", + healthy = true + ) + } + + // Zusätzlicher Endpunkt um die DB zu prüfen (History) + @GetMapping("/ping/history") + fun getHistory() = pingUseCase.getPingHistory().map { + mapOf("id" to it.id.toString(), "message" to it.message, "time" to it.timestamp.toString()) + } +} diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingController.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingController.kt deleted file mode 100644 index 472788e4..00000000 --- a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingController.kt +++ /dev/null @@ -1,40 +0,0 @@ -package at.mocode.ping.service - -import at.mocode.ping.api.EnhancedPingResponse -import at.mocode.ping.api.HealthResponse -import at.mocode.ping.api.PingApi -import at.mocode.ping.api.PingResponse -import org.springframework.web.bind.annotation.* -import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter - -@RestController -@CrossOrigin( - origins = ["http://localhost:8080", "http://localhost:8083", "http://localhost:4000"], - methods = [RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS], - allowedHeaders = ["*"], - allowCredentials = "true" -) -class PingController( - private val pingService: PingServiceCircuitBreaker -) : PingApi { - - // Contract endpoints - @GetMapping("/ping/simple") - override suspend fun simplePing(): PingResponse { - val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) - return PingResponse( - status = "pong", - timestamp = now, - service = "ping-service" - ) - } - - @GetMapping("/ping/enhanced") - override suspend fun enhancedPing( - @RequestParam(required = false, defaultValue = "false") simulate: Boolean - ): EnhancedPingResponse = pingService.ping(simulate) - - @GetMapping("/ping/health") - override suspend fun healthCheck(): HealthResponse = pingService.healthCheck() -} diff --git a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceCircuitBreaker.kt b/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceCircuitBreaker.kt deleted file mode 100644 index f257998e..00000000 --- a/backend/services/ping/ping-service/src/main/kotlin/at/mocode/ping/service/PingServiceCircuitBreaker.kt +++ /dev/null @@ -1,109 +0,0 @@ -package at.mocode.ping.service - -import at.mocode.ping.api.EnhancedPingResponse -import at.mocode.ping.api.HealthResponse -import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import kotlin.random.Random - -/** - * Service demonstrating a Circuit Breaker pattern with Resilience - * - * This service simulates potential failures and uses circuit breaker - * to handle service degradation gracefully with fallback responses. - */ -@Service -class PingServiceCircuitBreaker { - - private val logger = LoggerFactory.getLogger(PingServiceCircuitBreaker::class.java) - - companion object { - const val PING_CIRCUIT_BREAKER = "pingCircuitBreaker" - private val formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME //.ofPattern("yyyy-MM-dd HH:mm:ss") - } - - /** - * Primary ping method with circuit breaker protection returning DTO directly - * - * @param simulateFailure - if true, randomly throws exceptions to test circuit breaker - */ - @CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackPing") - fun ping(simulateFailure: Boolean = false): EnhancedPingResponse { - val start = System.nanoTime() - logger.info("Executing ping service call...") - - if (simulateFailure && Random.nextDouble() < 0.6) { - logger.warn("Simulating service failure for circuit breaker testing") - throw RuntimeException("Simulated service failure") - } - - val currentTime = LocalDateTime.now().atOffset(java.time.ZoneOffset.UTC).format(formatter) - val elapsedMs = (System.nanoTime() - start) / 1_000_000 - logger.info("Ping service call successful") - - return EnhancedPingResponse( - status = "pong", - timestamp = currentTime, - service = "ping-service", - circuitBreakerState = "CLOSED", - responseTime = elapsedMs - ) - } - - /** - * Fallback method called when circuit breaker is OPEN - * - * @param simulateFailure - original parameter (ignored in fallback) - * @param exception - the exception that triggered the fallback - */ - fun fallbackPing(simulateFailure: Boolean = false, exception: Exception): EnhancedPingResponse { - val start = System.nanoTime() - // Die volle Exception nur loggen, nicht an den Client weitergeben. - logger.warn("Circuit breaker fallback triggered due to: {}", exception.toString()) - - val currentTime = LocalDateTime.now().atOffset(java.time.ZoneOffset.UTC).format(formatter) - val elapsedMs = (System.nanoTime() - start) / 1_000_000 - - return EnhancedPingResponse( - status = "fallback", - timestamp = currentTime, - service = "ping-service-fallback", - circuitBreakerState = "OPEN", - responseTime = elapsedMs - ) - } - - /** - * Health check method with circuit breaker protection returning DTO directly - */ - @CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackHealth") - fun healthCheck(): HealthResponse { - logger.info("Executing health check...") - - val currentTime = LocalDateTime.now().atOffset(java.time.ZoneOffset.UTC).format(formatter) - return HealthResponse( - status = "pong", - timestamp = currentTime, - service = "ping-service", - healthy = true - ) - } - - /** - * Fallback for health check returning DTO - */ - fun fallbackHealth(exception: Exception): HealthResponse { - logger.warn("Health check fallback triggered: {}", exception.message) - - val currentTime = LocalDateTime.now().atOffset(java.time.ZoneOffset.UTC).format(formatter) - return HealthResponse( - status = "down", - timestamp = currentTime, - service = "ping-service", - healthy = false - ) - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f7cb6cb9..f044e472 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,8 +16,9 @@ kotlinx-coroutines = "1.10.2" # --- Spring Ecosystem --- -springBoot = "4.0.1" -# Spring Cloud Version kompatibel zu Spring Boot 4.0.1 +# KORREKTUR: Version auf Benutzerwunsch angepasst (war 4.0.1) +springBoot = "3.5.9" +# Spring Cloud Version kompatibel zu Spring Boot springCloud = "2025.1.0" # springCloudGateway = "4.3.0" springDependencyManagement = "1.1.7" @@ -153,8 +154,8 @@ spring-boot-starter-oauth2-resource-server = { module = "org.springframework.boo spring-boot-starter-security = { module = "org.springframework.boot:spring-boot-starter-security" } spring-boot-starter-webflux = { module = "org.springframework.boot:spring-boot-starter-webflux" } spring-boot-starter-json = { module = "org.springframework.boot:spring-boot-starter-json" } -# KORREKTUR: Fehlende Definition für spring-boot-starter-aop hinzugefügt. -spring-boot-starter-aop = { module = "org.springframework.boot:spring-boot-starter-aop" } +# KORREKTUR: Explizite Versionierung über BOM-Referenz +spring-boot-starter-aop = { module = "org.springframework.boot:spring-boot-starter-aop", version.ref = "springBoot" } spring-kafka = { module = "org.springframework.kafka:spring-kafka" } spring-security-oauth2-jose = { module = "org.springframework.security:spring-security-oauth2-jose" } spring-web = { module = "org.springframework:spring-web" }