├── gradle.properties ├── platform-spring-bom ├── platform-spring-scheduling │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── application-noop.properties │ │ │ │ └── logback-test.xml │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── scheduling │ │ │ │ ├── redis │ │ │ │ ├── TestApplication.kt │ │ │ │ └── ApplicationTest.kt │ │ │ │ └── noop │ │ │ │ ├── TestApplication.kt │ │ │ │ └── ApplicationTest.kt │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── scheduling │ │ │ ├── lock │ │ │ ├── NoopLockProvider.kt │ │ │ └── NoopLock.kt │ │ │ └── autoconfigure │ │ │ └── SchedulingAutoConfiguration.kt │ └── build.gradle ├── platform-spring-monitoring │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ └── application-none.yaml │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── monitoring │ │ │ │ ├── ManagementSecurityIntegrationSpringSecurityTest.kt │ │ │ │ ├── ManagementSecurityIntegrationNoneSpringSecurityTest.kt │ │ │ │ └── autoconfigure │ │ │ │ └── ManagementServerPortEnvironmentPostProcessorTest.kt │ │ └── main │ │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ ├── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ └── spring.factories │ │ │ └── config │ │ │ │ └── application.yaml │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── monitoring │ │ │ └── autoconfigure │ │ │ └── ManagementSecurityAutoConfiguration.kt │ └── build.gradle ├── platform-spring-i18n │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── messages_en.properties │ │ │ │ ├── application.yaml │ │ │ │ └── messages_ja.properties │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── i18n │ │ │ │ ├── I18nTestApplication.kt │ │ │ │ └── I18nApplicationTest.kt │ │ └── main │ │ │ ├── kotlin │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── i18n │ │ │ │ ├── LocaleInformation.kt │ │ │ │ ├── I18nService.kt │ │ │ │ ├── LocaleAccess.kt │ │ │ │ ├── autoconfigure │ │ │ │ ├── I18nProperties.kt │ │ │ │ ├── PlatformI18nWebAutoConfiguration.kt │ │ │ │ └── PlatformI18nAutoConfiguration.kt │ │ │ │ ├── ListResourceBundleMessageSource.kt │ │ │ │ ├── MessageSourceUtils.kt │ │ │ │ ├── impl │ │ │ │ ├── I18nServiceImpl.kt │ │ │ │ └── PlatformMessageSourceImpl.kt │ │ │ │ └── controller │ │ │ │ └── I18nController.kt │ │ │ └── resources │ │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── build.gradle ├── platform-spring-context │ ├── build.gradle │ └── src │ │ └── main │ │ └── kotlin │ │ └── io │ │ └── cloudflight │ │ └── platform │ │ └── spring │ │ ├── autoconfigure │ │ └── azurite │ │ │ └── AzuriteConnectionDetails.kt │ │ └── context │ │ └── ApplicationContextProfiles.kt ├── platform-spring-caching │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ └── application-testcontainer.yaml │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ ├── caching │ │ │ │ ├── CachingTestApplication.kt │ │ │ │ ├── RedisContainerIntegrationTest.kt │ │ │ │ ├── CachingConfigurerAutoConfigurationTest.kt │ │ │ │ └── EvictCacheErrorHandlerTest.kt │ │ │ │ └── spring │ │ │ │ └── security │ │ │ │ └── TestSerialVersionId.kt │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── caching │ │ │ ├── EvictCacheErrorHandler.kt │ │ │ ├── serializer │ │ │ └── SafeRedisSessionSerializer.kt │ │ │ └── autoconfigure │ │ │ ├── CachingConfigurerAutoConfiguration.kt │ │ │ └── SessionAutoConfiguration.kt │ └── build.gradle ├── platform-spring-cloud-storage │ ├── platform-spring-cloud-storage-api │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── storage │ │ │ ├── dto │ │ │ ├── ObjectProperties.kt │ │ │ ├── StorageRequestType.kt │ │ │ ├── StorageLocation.kt │ │ │ ├── StorageRequest.kt │ │ │ └── StorageObjectNotFoundException.kt │ │ │ └── service │ │ │ └── StorageService.kt │ └── platform-spring-cloud-storage-azure │ │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── storage │ │ │ │ └── azure │ │ │ │ ├── service │ │ │ │ ├── SasTokenStrategy.kt │ │ │ │ ├── AzuriteSasTokenStrategy.kt │ │ │ │ └── AzureSasTokenStrategy.kt │ │ │ │ └── autoconfigure │ │ │ │ └── PlatformAzureStorageBlobAutoConfiguration.kt │ │ └── test │ │ │ ├── resources │ │ │ ├── upload.pdf │ │ │ └── application-test.yaml │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── storage │ │ │ └── azure │ │ │ ├── TestApplication.kt │ │ │ └── Controller.kt │ │ └── build.gradle ├── platform-spring-validation-api │ ├── build.gradle │ └── src │ │ └── main │ │ └── kotlin │ │ └── io │ │ └── cloudflight │ │ └── platform │ │ └── spring │ │ └── validation │ │ └── api │ │ ├── dto │ │ ├── MessageSeverity.kt │ │ ├── Message.kt │ │ ├── GlobalMessageDto.kt │ │ ├── ErrorResponse.kt │ │ └── FieldMessageDto.kt │ │ └── ValidationOpenApiGenerator.kt ├── platform-spring-logging │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── META-INF │ │ │ │ │ └── spring │ │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ └── io │ │ │ │ │ └── cloudflight │ │ │ │ │ └── platform │ │ │ │ │ └── spring │ │ │ │ │ └── logging │ │ │ │ │ └── clf-base.xml │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── cloudflight │ │ │ │ │ └── platform │ │ │ │ │ └── spring │ │ │ │ │ └── logging │ │ │ │ │ ├── annotation │ │ │ │ │ ├── LogParams.java │ │ │ │ │ └── LogParam.java │ │ │ │ │ ├── service │ │ │ │ │ └── LogFlusher.java │ │ │ │ │ ├── autoconfigure │ │ │ │ │ └── LoggingServiceAutoConfiguration.java │ │ │ │ │ └── interceptor │ │ │ │ │ └── LogParamInterceptor.java │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── logging │ │ │ │ └── mdc │ │ │ │ ├── mdcScopeFunction.kt │ │ │ │ ├── MDCScope.kt │ │ │ │ └── impl │ │ │ │ └── MDCScopeImpl.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── logging │ │ │ ├── interceptor │ │ │ ├── BeanInterface.kt │ │ │ ├── LoggingApplication.kt │ │ │ ├── BeanImplementation.kt │ │ │ ├── MySpringBean.kt │ │ │ └── LoggingApplicationTest.kt │ │ │ └── mdc │ │ │ └── MdcScopeFunctionTest.kt │ └── build.gradle ├── platform-spring-messaging │ ├── src │ │ ├── main │ │ │ ├── kotlin │ │ │ │ └── io │ │ │ │ │ └── cloudflight │ │ │ │ │ └── platform │ │ │ │ │ └── spring │ │ │ │ │ └── messaging │ │ │ │ │ ├── Message.kt │ │ │ │ │ ├── ProcessControlRegistry.kt │ │ │ │ │ ├── MessageBrokerService.kt │ │ │ │ │ ├── impl │ │ │ │ │ ├── MessageBrokerServiceImpl.kt │ │ │ │ │ └── RabbitListenerControlService.kt │ │ │ │ │ └── autoconfigure │ │ │ │ │ └── PlatformMessagingAutoConfiguration.kt │ │ │ └── resources │ │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test │ │ │ ├── resources │ │ │ └── application.yaml │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── messaging │ │ │ ├── MessagingTestApplication.kt │ │ │ └── MessagingContainerTest.kt │ └── build.gradle ├── platform-spring-tracing │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── tracing │ │ │ ├── filter │ │ │ ├── RequestLoggingDataProvider.kt │ │ │ └── RequestLoggingFilter.kt │ │ │ └── autoconfigure │ │ │ └── PlatformTracingAutoConfiguration.kt │ └── build.gradle ├── platform-spring-server-config │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── server │ │ │ │ ├── autoconfigure │ │ │ │ └── PlatformServerAutoConfiguration.kt │ │ │ │ ├── ServerModuleIdentification.kt │ │ │ │ ├── ApplicationStartupPrinter.kt │ │ │ │ └── PlatformServerModuleStartupListener.kt │ │ └── test │ │ │ ├── resources │ │ │ └── logback-test.xml │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── server │ │ │ ├── PlatformTestApplication.kt │ │ │ ├── PlatformServerModuleStartupListenerTest.kt │ │ │ └── ServerStartupTest.kt │ └── build.gradle ├── platform-spring-validation │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── validation │ │ │ │ ├── ValidationConstants.kt │ │ │ │ ├── ErrorResponseFactory.kt │ │ │ │ ├── ErrorResponseMapper.kt │ │ │ │ ├── ValidationException.kt │ │ │ │ ├── impl │ │ │ │ ├── ErrorResponseFactoryImpl.kt │ │ │ │ └── ErrorResponseMapperImpl.kt │ │ │ │ ├── autoconfigure │ │ │ │ └── PlatformValidationAutoConfiguration.kt │ │ │ │ └── ValidationExceptionAdvice.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── validation │ │ │ └── impl │ │ │ └── ErrorResponseMapperTest.kt │ └── build.gradle ├── platform-spring-jpa │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── jpa │ │ │ └── autoconfigure │ │ │ ├── JpaAutoConfiguration.kt │ │ │ ├── TransactionCustomizer.kt │ │ │ ├── TransactionProperties.kt │ │ │ └── QueryDslAutoConfiguration.kt │ └── build.gradle ├── platform-spring-logging-server-config │ ├── build.gradle │ └── src │ │ ├── test │ │ ├── resources │ │ │ ├── logback-test.xml │ │ │ └── logback-test-json-splitter.xml │ │ └── java │ │ │ ├── io │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── logging │ │ │ │ ├── LoggingConfigTest.java │ │ │ │ └── LoggingJsonSplitterTest.java │ │ │ └── com │ │ │ └── latch │ │ │ └── LoggingEventClonerTest.java │ │ └── main │ │ ├── resources │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── logging │ │ │ └── clf-json-appender.xml │ │ └── java │ │ └── com │ │ └── latch │ │ ├── LoggingEventCloner.java │ │ ├── SplittingAppenderBase.java │ │ └── LengthSplittingAppender.java ├── platform-spring-json │ ├── build.gradle │ └── src │ │ └── main │ │ └── kotlin │ │ └── io │ │ └── cloudflight │ │ └── platform │ │ └── spring │ │ └── json │ │ └── ObjectMapperFactory.kt └── build.gradle ├── platform-spring-test-bom ├── platform-spring-test-archunit │ ├── build.gradle │ ├── archunit_store │ │ └── knownCleanCodeRules.txt │ └── src │ │ ├── test │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── test │ │ │ └── archunit │ │ │ └── ArchitectureTest.kt │ │ └── main │ │ └── kotlin │ │ └── io │ │ └── cloudflight │ │ └── platform │ │ └── spring │ │ └── test │ │ └── archunit │ │ └── AbstractCleanCodeTest.kt ├── platform-spring-test-jpa │ ├── src │ │ ├── test │ │ │ └── kotlin │ │ │ │ └── readme.txt │ │ └── main │ │ │ └── resources │ │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── build.gradle ├── platform-spring-test-bdd │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── services │ │ │ │ │ └── org.junit.jupiter.api.extension.Extension │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── test │ │ │ │ └── jgiven │ │ │ │ ├── Ticket.kt │ │ │ │ └── TicketDescriptionGenerator.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── test │ │ │ └── jgiven │ │ │ └── TicketDescriptionGeneratorTest.kt │ └── build.gradle ├── platform-spring-test │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── META-INF │ │ │ │ │ └── spring.factories │ │ │ │ └── junit-platform.properties │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── test │ │ │ │ ├── openfeign │ │ │ │ └── FeignTestClientFactory.kt │ │ │ │ └── context │ │ │ │ └── ApplicationStartupCustomizerFactory.kt │ │ └── test │ │ │ ├── resources │ │ │ └── logback-test.xml │ │ │ └── kotlin │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── test │ │ │ └── openfeign │ │ │ └── FeignTestClientFactoryTest.kt │ └── build.gradle ├── platform-spring-test-testcontainers │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── spring.factories │ │ │ └── kotlin │ │ │ │ └── io │ │ │ │ └── cloudflight │ │ │ │ └── platform │ │ │ │ └── spring │ │ │ │ └── test │ │ │ │ └── testcontainers │ │ │ │ ├── redis │ │ │ │ └── RedisContainer.kt │ │ │ │ └── azurite │ │ │ │ ├── AzuriteContainer.kt │ │ │ │ └── AzuriteContainerConnectionDetailsFactory.kt │ │ └── test │ │ │ └── java │ │ │ └── io │ │ │ └── cloudflight │ │ │ └── platform │ │ │ └── spring │ │ │ └── test │ │ │ └── testcontainers │ │ │ └── JUnit4ClasspathTest.java │ └── build.gradle └── build.gradle ├── licenses.json ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── .gitignore ├── .github ├── workflows │ ├── dependency-submission.yml │ ├── github-build.yml │ └── github-publish.yml └── dependabot.yml ├── settings.gradle └── gradlew.bat /gradle.properties: -------------------------------------------------------------------------------- 1 | kapt.include.compile.classpath=false 2 | kotlin.code.style=official -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/test/resources/application-noop.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/test/resources/application-none.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8090 -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-archunit/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api(libs.archunit.ccv) 3 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/test/resources/messages_en.properties: -------------------------------------------------------------------------------- 1 | testMessage=testMessageValue-english 2 | -------------------------------------------------------------------------------- /licenses.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artifact": "org.hdrhistogram:HdrHistogram:2.1.12", 4 | "license_id": "CC0-1.0" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflightio/cloudflight-platform-spring/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-jpa/src/test/kotlin/readme.txt: -------------------------------------------------------------------------------- 1 | empty file to apply kotlin plugin, we don't have sources here 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/* 2 | !.idea/runConfigurations/ 3 | *.iml 4 | 5 | .gradle 6 | build 7 | out 8 | 9 | jgiven-reports 10 | 11 | bin 12 | 13 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-context/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation("org.springframework.boot:spring-boot-autoconfigure") 3 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/test/resources/application-testcontainer.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | cache: 3 | redis: 4 | time-to-live: PT5M 5 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-api/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api("org.springframework:spring-web") 3 | } -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-bdd/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension: -------------------------------------------------------------------------------- 1 | com.tngtech.jgiven.junit5.JGivenExtension -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation-api/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation 'io.swagger:swagger-annotations' 3 | 4 | implementation 'org.springframework:spring-web' 5 | } 6 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-jpa/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | org.quickperf.spring.boot.QuickPerfProxyBeanAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-jpa/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(':platform-spring-test-bom:platform-spring-test') 3 | api 'org.quickperf:quick-perf-springboot2-sql-starter' 4 | } 5 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.logging.autoconfigure.LoggingServiceAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/main/kotlin/io/cloudflight/platform/spring/messaging/Message.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging 2 | 3 | interface Message { 4 | val queueName: String 5 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-tracing/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.tracing.autoconfigure.PlatformTracingAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.scheduling.autoconfigure.SchedulingAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.server.autoconfigure.PlatformServerAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.test.context.ContextCustomizerFactory=\ 2 | io.cloudflight.platform.spring.test.context.ApplicationStartupCustomizerFactory -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.messaging.autoconfigure.PlatformMessagingAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.monitoring.autoconfigure.ManagementSecurityAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.validation.autoconfigure.PlatformValidationAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test/src/main/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | # https://github.com/quick-perf/doc/wiki/JUnit-5#automatically-register-quickperf-extension 2 | junit.jupiter.extensions.autodetection.enabled=true -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | rabbitmq: 3 | listener: 4 | type: simple 5 | simple: 6 | concurrency: 1 7 | prefetch: 1 8 | auto-startup: false -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.env.EnvironmentPostProcessor=\ 2 | io.cloudflight.platform.spring.monitoring.autoconfigure.ManagementServerPortEnvironmentPostProcessor 3 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | 2 | cloudflight: 3 | i18n: 4 | locales: 5 | - JAPANESE 6 | - ENGLISH 7 | primary: JAPANESE 8 | 9 | spring: 10 | messages: 11 | basename: classpath:/messages 12 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/ValidationConstants.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation 2 | 3 | object ValidationConstants { 4 | 5 | const val VALIDATION_ADVICE_ORDER = 100 6 | } 7 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-api/src/main/kotlin/io/cloudflight/platform/spring/storage/dto/ObjectProperties.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.dto 2 | 3 | data class ObjectProperties( 4 | val etag: String? 5 | ) 6 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.storage.azure.autoconfigure.PlatformAzureStorageBlobAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-jpa/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.jpa.autoconfigure.JpaAutoConfiguration 2 | io.cloudflight.platform.spring.jpa.autoconfigure.QueryDslAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation-api/src/main/kotlin/io/cloudflight/platform/spring/validation/api/dto/MessageSeverity.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.api.dto 2 | 3 | enum class MessageSeverity { 4 | INFO, 5 | WARNING, 6 | ERROR 7 | } 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-api/src/main/kotlin/io/cloudflight/platform/spring/storage/dto/StorageRequestType.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.dto 2 | 3 | enum class StorageRequestType { 4 | GET, 5 | PUT 6 | } 7 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/LocaleInformation.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n 2 | 3 | data class LocaleInformation( 4 | val activeLocale: String, 5 | val availableLocales: List 6 | ) 7 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation-api/src/main/kotlin/io/cloudflight/platform/spring/validation/api/dto/Message.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.api.dto 2 | 3 | interface Message { 4 | val message: String 5 | val severity: MessageSeverity 6 | } 7 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/test/resources/messages_ja.properties: -------------------------------------------------------------------------------- 1 | testMessage=testMessageValue-japanese 2 | testMessagePlaceholderFrontend=testMessageValueWithFrontendPlaceholder: {{somePlaceholder}} 3 | testMessagePlaceholderBackend=testMessageValueWithBackendPlaceholder: {0} 4 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/I18nService.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n 2 | 3 | import java.util.* 4 | 5 | interface I18nService { 6 | 7 | val availableLocales: List 8 | val primaryLocale: Locale? 9 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/test/resources/upload.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflightio/cloudflight-platform-spring/HEAD/platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/test/resources/upload.pdf -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.i18n.autoconfigure.PlatformI18nAutoConfiguration 2 | io.cloudflight.platform.spring.i18n.autoconfigure.PlatformI18nWebAutoConfiguration -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/test/kotlin/io/cloudflight/platform/spring/caching/CachingTestApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | 5 | @SpringBootApplication 6 | class CachingTestApplication -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-bdd/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(':platform-spring-test-bom:platform-spring-test') 3 | 4 | api(libs.jgiven.junit5) 5 | api(libs.jgiven.spring.junit5) 6 | api(libs.jgiven.kotlin) 7 | 8 | runtimeOnly(libs.jgiven.html5.report) 9 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':platform-spring-bom:platform-spring-logging') 3 | 4 | // encoder for JSON logging 5 | runtimeOnly(libs.logback.logstash.encoder) 6 | 7 | implementation(libs.logback.classic) 8 | } 9 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-api/src/main/kotlin/io/cloudflight/platform/spring/storage/dto/StorageLocation.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.dto 2 | 3 | data class StorageLocation( 4 | val container: String, 5 | val objectName: String 6 | ) 7 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/test/kotlin/io/cloudflight/platform/spring/server/PlatformTestApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.server 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | 5 | @SpringBootApplication 6 | class PlatformTestApplication 7 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/test/kotlin/io/cloudflight/platform/spring/logging/interceptor/BeanInterface.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.interceptor 2 | 3 | interface BeanInterface { 4 | 5 | fun sayHello(name: String) 6 | fun sayHelloWithNamedParameter(name: String) 7 | } 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/test/kotlin/io/cloudflight/platform/spring/scheduling/redis/TestApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.scheduling.redis 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | 5 | @SpringBootApplication 6 | class TestApplication 7 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-testcontainers/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Connection Details Factories 2 | org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\ 3 | io.cloudflight.platform.spring.test.testcontainers.azurite.AzuriteContainerConnectionDetailsFactory -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/test/kotlin/io/cloudflight/platform/spring/logging/interceptor/LoggingApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.interceptor 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | 5 | @SpringBootApplication 6 | class LoggingApplication 7 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-testcontainers/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(":platform-spring-bom:platform-spring-context") 3 | api("org.testcontainers:junit-jupiter") 4 | api("org.springframework.boot:spring-boot-testcontainers") 5 | api(libs.testcontainers.junit4.mock) 6 | } -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation-api/src/main/kotlin/io/cloudflight/platform/spring/validation/api/dto/GlobalMessageDto.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.api.dto 2 | 3 | data class GlobalMessageDto( 4 | override val message: String, 5 | override val severity: MessageSeverity 6 | ) : Message 7 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/test/kotlin/io/cloudflight/platform/spring/storage/azure/TestApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.azure 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | 5 | @SpringBootApplication 6 | class TestApplication -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-json/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api('com.fasterxml.jackson.core:jackson-annotations') 3 | api('com.fasterxml.jackson.core:jackson-databind') 4 | 5 | api('com.fasterxml.jackson.module:jackson-module-kotlin') 6 | 7 | implementation('org.springframework.boot:spring-boot-starter-json') 8 | } 9 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-api/src/main/kotlin/io/cloudflight/platform/spring/storage/dto/StorageRequest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.dto 2 | 3 | data class StorageRequest( 4 | val url: String, 5 | val requestType: StorageRequestType, 6 | val httpHeaders: Map 7 | ) 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation-api/src/main/kotlin/io/cloudflight/platform/spring/validation/api/dto/ErrorResponse.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.api.dto 2 | 3 | data class ErrorResponse( 4 | val fieldMessages: List = emptyList(), 5 | val globalMessages: List = emptyList() 6 | ) 7 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-archunit/archunit_store/knownCleanCodeRules.txt: -------------------------------------------------------------------------------- 1 | logging.no-streams 2 | logging.no-java-util-logging 3 | logging.static-final-loggers 4 | logging.do-not-expose-loggers-via-methods 5 | logging.do-not-extend-klogging 6 | jdk.no-generic-exceptions 7 | jdk.no-jodatime 8 | jdk.bigdecimal-do-not-call-constructor-with-double-parameter 9 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-api/src/main/kotlin/io/cloudflight/platform/spring/storage/dto/StorageObjectNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.dto 2 | 3 | class StorageObjectNotFoundException(val storageLocation: StorageLocation) : 4 | RuntimeException("The object $storageLocation could not be found") -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/LocaleAccess.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n 2 | 3 | import org.springframework.context.i18n.LocaleContextHolder 4 | import java.util.* 5 | 6 | interface LocaleAccess { 7 | val currentLocale: Locale 8 | get() = LocaleContextHolder.getLocale() 9 | } 10 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.cloudflight.platform.spring.caching.autoconfigure.CachingAutoConfiguration 2 | io.cloudflight.platform.spring.caching.autoconfigure.SessionAutoConfiguration 3 | io.cloudflight.platform.spring.caching.autoconfigure.CachingConfigurerAutoConfiguration 4 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(':platform-spring-bom:platform-spring-context') 3 | api project(':platform-spring-bom:platform-spring-monitoring') 4 | runtimeOnly project(':platform-spring-bom:platform-spring-logging-server-config') 5 | 6 | testImplementation project(':platform-spring-test-bom:platform-spring-test') 7 | } 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/test/kotlin/io/cloudflight/platform/spring/scheduling/noop/TestApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.scheduling.noop 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 5 | 6 | @SpringBootApplication(exclude = [RedisAutoConfiguration::class]) 7 | class TestApplication 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/test/kotlin/io/cloudflight/platform/spring/i18n/I18nTestApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n 2 | 3 | import io.cloudflight.platform.spring.i18n.autoconfigure.PlatformI18nAutoConfiguration 4 | import org.springframework.boot.autoconfigure.SpringBootApplication 5 | 6 | @SpringBootApplication(scanBasePackageClasses = [PlatformI18nAutoConfiguration::class]) 7 | class I18nTestApplication 8 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/test/resources/logback-test-json-splitter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/ErrorResponseFactory.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation 2 | 3 | import io.cloudflight.platform.spring.validation.api.dto.ErrorResponse 4 | 5 | interface ErrorResponseFactory { 6 | 7 | fun createFieldMessageResponse(field: String, code: String): ErrorResponse 8 | 9 | fun createGlobalMessageResponse(code: String): ErrorResponse 10 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/autoconfigure/I18nProperties.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n.autoconfigure 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties 4 | import java.util.* 5 | 6 | @ConfigurationProperties(prefix = "cloudflight.i18n") 7 | class I18nProperties { 8 | var locales: List = listOf(Locale.GERMAN) 9 | var primary: Locale? = null 10 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-context/src/main/kotlin/io/cloudflight/platform/spring/autoconfigure/azurite/AzuriteConnectionDetails.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.autoconfigure.azurite 2 | 3 | import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails 4 | 5 | interface AzuriteConnectionDetails : ConnectionDetails { 6 | 7 | val accountEndpoint: String 8 | 9 | val accountName: String 10 | 11 | val accountKey: String 12 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/java/io/cloudflight/platform/spring/logging/annotation/LogParams.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * Container annotation that aggregates several {@link LogParam} annotations. 7 | */ 8 | @Target(ElementType.PARAMETER) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | public @interface LogParams { 12 | LogParam[] value(); 13 | } 14 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/test/resources/application-test.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | azure: 4 | storage: 5 | blob: 6 | enabled: true 7 | compatibility-verifier: 8 | enabled: false # TODO remove as soon as the ms-azurite library is final in version 6.0.0, see https://github.com/Azure/azure-sdk-for-java/wiki/Spring-Versions-Mapping 9 | main: 10 | allow-bean-definition-overriding: true -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/main/kotlin/io/cloudflight/platform/spring/messaging/ProcessControlRegistry.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging 2 | 3 | import org.springframework.context.ApplicationEvent 4 | 5 | // TODO move to other package, add documentation 6 | interface ProcessControlRegistry { 7 | fun actionEnabled(id: String): Boolean 8 | } 9 | 10 | data class ProcessControlEvent(val actionId: String, val enabled: Boolean) : ApplicationEvent(actionId) -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-tracing/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':platform-spring-bom:platform-spring-logging') 3 | api('io.opentelemetry:opentelemetry-api') 4 | implementation 'org.springframework.boot:spring-boot-autoconfigure' 5 | implementation 'org.springframework.boot:spring-boot-starter-web' 6 | 7 | implementation(libs.logback.logstash.encoder) 8 | 9 | kapt 'org.springframework.boot:spring-boot-configuration-processor' 10 | } 11 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-archunit/src/test/kotlin/io/cloudflight/platform/spring/test/archunit/ArchitectureTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.archunit 2 | 3 | import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests 4 | import com.tngtech.archunit.junit.AnalyzeClasses 5 | 6 | @AnalyzeClasses(packagesOf = [ArchitectureTest::class], importOptions = [DoNotIncludeTests::class]) 7 | class ArchitectureTest : AbstractCleanCodeTest() { 8 | // your ArchUnit tests go here 9 | } -------------------------------------------------------------------------------- /.github/workflows/dependency-submission.yml: -------------------------------------------------------------------------------- 1 | name: Dependency Submission 2 | 3 | on: 4 | push: 5 | branches: [ 'master' ] 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | dependency-submission: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-java@v4 16 | with: 17 | distribution: temurin 18 | java-version: 17 19 | 20 | - name: Generate and submit dependency graph 21 | uses: gradle/actions/dependency-submission@v3 22 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/test/java/io/cloudflight/platform/spring/logging/LoggingConfigTest.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class LoggingConfigTest { 8 | 9 | private static final Logger LOG = LoggerFactory.getLogger(LoggingConfigTest.class); 10 | 11 | @Test 12 | void logWithCloudbackConfig() { 13 | LOG.info("Hello World"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-tracing/src/main/kotlin/io/cloudflight/platform/spring/tracing/filter/RequestLoggingDataProvider.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.tracing.filter 2 | 3 | import jakarta.servlet.http.HttpServletRequest 4 | import org.slf4j.MDC 5 | 6 | /** 7 | * Implement this interface in order to add data to the [MDC] for every request by means of the [RequestLoggingFilter] 8 | */ 9 | interface RequestLoggingDataProvider { 10 | 11 | fun provideData(request: HttpServletRequest): Map 12 | } 13 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':platform-spring-bom:platform-spring-json') 3 | implementation project(':platform-spring-bom:platform-spring-logging') 4 | 5 | testImplementation project(':platform-spring-test-bom:platform-spring-test') 6 | 7 | kapt 'org.springframework.boot:spring-boot-configuration-processor' 8 | implementation 'org.springframework.boot:spring-boot-starter-web' 9 | testImplementation 'org.springframework.boot:spring-boot-test-autoconfigure' 10 | } 11 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/main/kotlin/io/cloudflight/platform/spring/storage/azure/service/SasTokenStrategy.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.azure.service 2 | 3 | import com.azure.storage.blob.BlobClient 4 | import com.azure.storage.blob.sas.BlobContainerSasPermission 5 | import java.time.Duration 6 | 7 | interface SasTokenStrategy { 8 | 9 | fun getSasToken( 10 | blobClient: BlobClient, 11 | validityDuration: Duration, 12 | permission: BlobContainerSasPermission? 13 | ): String 14 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation-api/src/main/kotlin/io/cloudflight/platform/spring/validation/api/dto/FieldMessageDto.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.api.dto 2 | 3 | import io.swagger.annotations.ApiModel 4 | import io.swagger.annotations.ApiModelProperty 5 | 6 | @ApiModel(description = "validation messages on a field level") 7 | data class FieldMessageDto( 8 | @ApiModelProperty("the field name where this validation message is applied") 9 | val field: String, 10 | private val fieldMessage: GlobalMessageDto 11 | ) : Message by fieldMessage 12 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/kotlin/io/cloudflight/platform/spring/logging/mdc/mdcScopeFunction.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.mdc 2 | 3 | import io.cloudflight.platform.spring.logging.mdc.impl.MDCScopeImpl 4 | 5 | fun mdcScope(body: MDCScope.() -> T): T { 6 | return mdcScope(false, body) 7 | } 8 | 9 | fun mdcScope(ignoreNullValues: Boolean, body: MDCScope.() -> T): T { 10 | val mdcScope = MDCScopeImpl(ignoreNullValues) 11 | try { 12 | return mdcScope.body() 13 | } finally { 14 | mdcScope.removeAllAdded() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/test/kotlin/io/cloudflight/platform/spring/server/PlatformServerModuleStartupListenerTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.server 2 | 3 | import io.mockk.mockk 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.Test 6 | 7 | class PlatformServerModuleStartupListenerTest { 8 | 9 | @Test 10 | fun startup() { 11 | val identification: ServerModuleIdentification = PlatformServerModuleStartupListener(null, null, mockk()) 12 | assertThat(identification.getVersion()).isEqualTo("unknown") 13 | } 14 | } -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-testcontainers/src/test/java/io/cloudflight/platform/spring/test/testcontainers/JUnit4ClasspathTest.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.testcontainers; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.fail; 6 | 7 | public class JUnit4ClasspathTest { 8 | 9 | @Test 10 | public void noJunit4OnClasspath() { 11 | try { 12 | Class.forName("org.junit.Before"); 13 | fail("JUnit4 must not be on the classpath"); 14 | } catch (ClassNotFoundException e) { 15 | // ok 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/kotlin/io/cloudflight/platform/spring/logging/mdc/MDCScope.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.mdc 2 | 3 | interface MDCAccess { 4 | fun put(key: String, value: String?) 5 | fun put(entry: Pair) 6 | fun remove(key: String) 7 | } 8 | 9 | interface MDCScope { 10 | 11 | /** 12 | * we want to call this property MDC on purpose to give the caller inside 13 | * the kotlin extension function the impression it calls the static MDC class 14 | * from SLF4J 15 | */ 16 | @Suppress("VariableNaming", "PropertyName") 17 | val MDC: MDCAccess 18 | } 19 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/test/kotlin/io/cloudflight/platform/spring/server/ServerStartupTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.server 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.boot.test.context.SpringBootTest 7 | 8 | @SpringBootTest 9 | class ServerStartupTest(@Autowired private val serverModuleIdentification: ServerModuleIdentification) { 10 | 11 | @Test 12 | fun startup() { 13 | assertThat(serverModuleIdentification.getGroup()).isNotBlank() 14 | } 15 | } -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-bdd/src/test/kotlin/io/cloudflight/platform/spring/test/jgiven/TicketDescriptionGeneratorTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.jgiven 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | class TicketDescriptionGeneratorTest { 7 | 8 | @Test 9 | fun useDefaultServer() { 10 | val descriptor = TicketDescriptionGenerator() 11 | val url = descriptor.generateDescription(null, null, "CLF-3") 12 | assertThat(url).isEqualTo("https://jira.unknown.com/browse/CLF-3") 13 | } 14 | } -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(':platform-spring-bom:platform-spring-context') 3 | api project(':platform-spring-bom:platform-spring-logging') 4 | api project(':platform-spring-bom:platform-spring-json') 5 | 6 | api 'org.springframework:spring-test' 7 | api 'org.springframework.boot:spring-boot-starter-test' 8 | api 'org.springframework.cloud:spring-cloud-starter-openfeign' 9 | 10 | api 'org.quickperf:quick-perf-junit5' 11 | 12 | testImplementation 'io.swagger:swagger-annotations' 13 | testImplementation 'org.springframework.boot:spring-boot-starter-web' 14 | } 15 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-json/src/main/kotlin/io/cloudflight/platform/spring/json/ObjectMapperFactory.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.json 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule 5 | import com.fasterxml.jackson.module.kotlin.registerKotlinModule 6 | 7 | /** 8 | * @author Harald Radi (harald.radi@cloudflight.io) 9 | * @version 1.0 10 | */ 11 | object ObjectMapperFactory { 12 | fun createObjectMapper(): ObjectMapper { 13 | return ObjectMapper() 14 | .registerModule(JavaTimeModule()) 15 | .registerKotlinModule() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/main/kotlin/io/cloudflight/platform/spring/scheduling/lock/NoopLockProvider.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.scheduling.lock 2 | 3 | import net.javacrumbs.shedlock.core.LockConfiguration 4 | import net.javacrumbs.shedlock.core.LockProvider 5 | import net.javacrumbs.shedlock.core.SimpleLock 6 | import java.util.* 7 | 8 | /** 9 | * @author Harald Radi (harald.radi@catalysts.cc) 10 | * @version 1.0 11 | */ 12 | class NoopLockProvider : LockProvider { 13 | override fun lock(lockConfiguration: LockConfiguration): Optional { 14 | return Optional.of(NoopLock(lockConfiguration)) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/ErrorResponseMapper.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation 2 | 3 | import io.cloudflight.platform.spring.validation.api.dto.ErrorResponse 4 | import org.springframework.validation.BindingResult 5 | 6 | /** 7 | * Mapper which is used to map any [BindingResult] to an [ErrorResponse] to be transported to clients. 8 | * 9 | * Providing a bean of this type allows customizing and extending the data provided in the [ErrorResponse]. 10 | */ 11 | interface ErrorResponseMapper { 12 | fun mapBindingResult(bindingResult: BindingResult): ErrorResponse 13 | } 14 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':platform-spring-bom:platform-spring-caching') 3 | 4 | api('net.javacrumbs.shedlock:shedlock-spring') 5 | implementation 'net.javacrumbs.shedlock:shedlock-provider-redis-spring' 6 | 7 | api('org.springframework:spring-context') 8 | 9 | implementation 'org.springframework.boot:spring-boot-starter-data-redis' 10 | 11 | testImplementation project(':platform-spring-test-bom:platform-spring-test-testcontainers') 12 | testImplementation("org.testcontainers:junit-jupiter") 13 | testImplementation("org.springframework.boot:spring-boot-testcontainers") 14 | 15 | } 16 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api(libs.kotlin.logging) 3 | 4 | // required for conditionals in logback 5 | runtimeOnly 'org.codehaus.janino:janino' 6 | 7 | api('org.slf4j:slf4j-api') 8 | 9 | implementation 'org.apache.commons:commons-lang3' 10 | implementation 'org.aspectj:aspectjweaver' 11 | 12 | implementation('org.slf4j:slf4j-api') 13 | 14 | implementation 'org.springframework.boot:spring-boot-starter-logging' 15 | implementation 'org.springframework.boot:spring-boot-autoconfigure' 16 | 17 | // required for org/springframework/boot/logging/logback/defaults.xml and 18 | implementation 'org.springframework.boot:spring-boot' 19 | } 20 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-bdd/src/main/kotlin/io/cloudflight/platform/spring/test/jgiven/Ticket.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.jgiven 2 | 3 | import com.tngtech.jgiven.annotation.IsTag 4 | 5 | /** 6 | * Use this annotation on a JUnit5 `@Test` method in order to link the test with one or more tickets in your 7 | * tracking system. 8 | * 9 | * Set the System.property `jgiven.issue.server.url` to exactly an URL like `https:///browse/%s`. 10 | */ 11 | @IsTag(descriptionGenerator = TicketDescriptionGenerator::class) 12 | annotation class Ticket( 13 | 14 | /** 15 | * One ore more ticket IDs, i.e. `CLFP-1` 16 | */ 17 | val value: Array 18 | ) 19 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/main/resources/config/application.yaml: -------------------------------------------------------------------------------- 1 | # TODO replace this file with according logic as per Spring Boot 2.4: https://spring.io/blog/2020/08/14/config-file-processing-in-spring-boot-2-4 2 | management: 3 | info: 4 | build: 5 | enabled: true 6 | git: 7 | enabled: true 8 | mode: full 9 | endpoint: 10 | health: 11 | show-details: always 12 | beans: 13 | enabled: true 14 | metrics: 15 | enabled: true 16 | prometheus: 17 | enabled: true 18 | metrics: 19 | export: 20 | prometheus: 21 | enabled: true 22 | endpoints: 23 | web: 24 | exposure: 25 | include: "*" 26 | health: 27 | livenessstate: 28 | enabled: true -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(':platform-spring-bom:platform-spring-validation-api') 3 | implementation project(':platform-spring-bom:platform-spring-i18n') 4 | implementation project(':platform-spring-bom:platform-spring-json') 5 | 6 | testImplementation project(':platform-spring-test-bom:platform-spring-test') 7 | 8 | api('jakarta.validation:jakarta.validation-api') 9 | 10 | kapt 'org.springframework.boot:spring-boot-configuration-processor' 11 | implementation 'org.springframework.boot:spring-boot-starter-validation' 12 | implementation 'org.springframework.boot:spring-boot-starter-web' 13 | testImplementation 'org.springframework.boot:spring-boot-test-autoconfigure' 14 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | groups: 13 | minor: 14 | patterns: 15 | - "*" 16 | update-types: 17 | - "minor" 18 | - "patch" 19 | major: 20 | patterns: 21 | - "*" 22 | update-types: 23 | - "major" 24 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/test/kotlin/io/cloudflight/platform/spring/logging/interceptor/BeanImplementation.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.interceptor 2 | 3 | import org.assertj.core.api.Assertions 4 | import org.slf4j.MDC 5 | import org.springframework.stereotype.Service 6 | 7 | @Service 8 | class BeanImplementation : BeanInterface { 9 | 10 | override fun sayHello(@io.cloudflight.platform.spring.logging.annotation.LogParam name: String) { 11 | Assertions.assertThat(MDC.get("name")).isEqualTo(name) 12 | } 13 | 14 | override fun sayHelloWithNamedParameter(@io.cloudflight.platform.spring.logging.annotation.LogParam(name = "myName") name: String) { 15 | Assertions.assertThat(MDC.get("myName")).isEqualTo(name) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':platform-spring-bom:platform-spring-context') 3 | 4 | implementation 'io.micrometer:micrometer-core' 5 | implementation 'io.micrometer:micrometer-registry-prometheus' 6 | 7 | compileOnly 'jakarta.servlet:jakarta.servlet-api' 8 | 9 | // API to be able to implement own HealthIndicators 10 | api 'org.springframework.boot:spring-boot-starter-actuator' 11 | 12 | compileOnly 'org.springframework.security:spring-security-config' 13 | compileOnly 'org.springframework.security:spring-security-web' 14 | 15 | testImplementation 'org.springframework.boot:spring-boot-starter-security' 16 | testImplementation 'org.springframework.boot:spring-boot-starter-web' 17 | } 18 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/main/kotlin/io/cloudflight/platform/spring/scheduling/lock/NoopLock.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.scheduling.lock 2 | 3 | import net.javacrumbs.shedlock.core.AbstractSimpleLock 4 | import net.javacrumbs.shedlock.core.LockConfiguration 5 | import net.javacrumbs.shedlock.core.SimpleLock 6 | import java.util.* 7 | 8 | 9 | /** 10 | * @author Harald Radi (harald.radi@catalysts.cc) 11 | * @version 1.0 12 | */ 13 | class NoopLock(lockConfiguration: LockConfiguration) : AbstractSimpleLock(lockConfiguration) { 14 | @Suppress("EmptyFunctionBlock") 15 | override fun doUnlock() { 16 | } 17 | 18 | override fun doExtend(newConfig: LockConfiguration): Optional { 19 | return Optional.of(this) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-archunit/src/main/kotlin/io/cloudflight/platform/spring/test/archunit/AbstractCleanCodeTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.archunit 2 | 3 | import com.tngtech.archunit.junit.ArchTest 4 | import com.tngtech.archunit.junit.ArchTests 5 | import io.cloudflight.cleancode.archunit.CleanCodeRuleSets 6 | import com.tngtech.archunit.junit.AnalyzeClasses 7 | 8 | /** 9 | * Extend from this test case and create your own ArchitectureTest and add the 10 | * [AnalyzeClasses] annotation there in order to automatically get all rules from 11 | * https://github.com/cloudflightio/archunit-cleancode-verifier. 12 | */ 13 | abstract class AbstractCleanCodeTest { 14 | @ArchTest 15 | val cleancode = ArchTests.`in`(CleanCodeRuleSets::class.java) 16 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/java/io/cloudflight/platform/spring/logging/service/LogFlusher.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.service; 2 | 3 | import ch.qos.logback.classic.LoggerContext; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.DisposableBean; 7 | 8 | /** 9 | * @author Thomas Reinthaler 10 | */ 11 | public class LogFlusher implements DisposableBean { 12 | private static final Logger LOG = LoggerFactory.getLogger(LogFlusher.class); 13 | 14 | @Override 15 | public void destroy() { 16 | LOG.info("Shutdown logger context."); 17 | final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 18 | loggerContext.stop(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/ListResourceBundleMessageSource.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n 2 | 3 | import org.springframework.context.MessageSource 4 | import java.util.* 5 | 6 | interface ListResourceBundleMessageSource : MessageSource { 7 | 8 | /** 9 | * Returns a map of key/value pairs of all configured message properties 10 | * (across all configured resource bundles). 11 | * Be aware that if 2 bundles specify the same key, the value of the first bundle 12 | * specifying a value for it, will be used, all later values will be ignored silently. 13 | * 14 | * @param locale current locale 15 | * @return key value pars 16 | */ 17 | fun getAllMessages(locale: Locale): Map 18 | } 19 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-jpa/src/main/kotlin/io/cloudflight/platform/spring/jpa/autoconfigure/JpaAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.jpa.autoconfigure 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfiguration 4 | import org.springframework.boot.context.properties.EnableConfigurationProperties 5 | import org.springframework.context.annotation.Bean 6 | import org.springframework.transaction.annotation.EnableTransactionManagement 7 | 8 | @AutoConfiguration 9 | @EnableTransactionManagement(order = 2000) 10 | @EnableConfigurationProperties(TransactionProperties::class) 11 | class JpaAutoConfiguration { 12 | 13 | @Bean 14 | fun platformTransactionCustomizer(properties: TransactionProperties): TransactionCustomizer { 15 | return TransactionCustomizer(properties) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-testcontainers/src/main/kotlin/io/cloudflight/platform/spring/test/testcontainers/redis/RedisContainer.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.testcontainers.redis 2 | 3 | import org.testcontainers.containers.GenericContainer 4 | import org.testcontainers.utility.DockerImageName 5 | 6 | class RedisContainer(dockerImageName: DockerImageName) : GenericContainer(dockerImageName) { 7 | 8 | @Deprecated(message = "pass a dockerImageName") 9 | constructor() : this(DockerImageName.parse(DEFAULT_IMAGE_NAME).withTag(DEFAULT_TAG)) 10 | 11 | init { 12 | addExposedPort(REDIS_PORT) 13 | } 14 | 15 | companion object { 16 | private val DEFAULT_IMAGE_NAME = "redis" 17 | 18 | private const val DEFAULT_TAG = "7.0.11" 19 | 20 | private const val REDIS_PORT = 6379 21 | } 22 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/test/kotlin/io/cloudflight/platform/spring/spring/security/TestSerialVersionId.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.spring.security 2 | 3 | import org.junit.jupiter.api.Assertions 4 | import org.junit.jupiter.api.Test 5 | import org.springframework.security.core.SpringSecurityCoreVersion 6 | 7 | /** 8 | * @author Harald Radi (harald.radi@cloudflight.io) 9 | * @version 1.0 10 | */ 11 | class TestSerialVersionId { 12 | @Test 13 | fun ensureSpringSecurityCoreVersion() { 14 | Assertions.assertEquals( 15 | 620L, 16 | SpringSecurityCoreVersion.SERIAL_VERSION_UID, 17 | "if that version changes, then http sessions aren't deserializable anymore. add a note to the " + 18 | "changelog and increase the version number accordingly to signal a breaking change." 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-jpa/src/main/kotlin/io/cloudflight/platform/spring/jpa/autoconfigure/TransactionCustomizer.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.jpa.autoconfigure 2 | 3 | import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizer 4 | import org.springframework.transaction.support.AbstractPlatformTransactionManager 5 | 6 | class TransactionCustomizer(private val properties: TransactionProperties) : TransactionManagerCustomizer { 7 | 8 | override fun customize(transactionManager: AbstractPlatformTransactionManager) { 9 | // we want to have that validation here in order to ensure that if a read-only transaction is being opened that 10 | // only readOnly subsequent calls are being done 11 | transactionManager.isValidateExistingTransaction = properties.validateExistingTransaction 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-jpa/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(':platform-spring-bom:platform-spring-context') 3 | 4 | api('jakarta.annotation:jakarta.annotation-api') 5 | api('jakarta.persistence:jakarta.persistence-api') 6 | api('jakarta.validation:jakarta.validation-api') 7 | 8 | api('org.springframework:spring-tx') 9 | 10 | api('org.springframework:spring-orm') 11 | 12 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 13 | 14 | api('org.springframework.data:spring-data-commons') 15 | 16 | api('org.springframework.data:spring-data-jpa') 17 | 18 | // Spring Data JPA requires the Kotlin-Reflect classes at runtime 19 | runtimeOnly('org.jetbrains.kotlin:kotlin-reflect') 20 | 21 | compileOnly('com.querydsl:querydsl-jpa::jakarta') 22 | 23 | kapt 'org.springframework.boot:spring-boot-configuration-processor' 24 | } 25 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/main/kotlin/io/cloudflight/platform/spring/messaging/MessageBrokerService.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging 2 | 3 | /** 4 | * Wrapper around [org.springframework.amqp.rabbit.core.RabbitTemplate] to send messages to RabbitMQ 5 | * 6 | * @author Klaus Lehner 7 | */ 8 | interface MessageBrokerService { 9 | 10 | /** 11 | * Sends the given message after transaction commit of the current transaction. This method will raise 12 | * an exception if we are currently not inside a transaction 13 | */ 14 | fun sendAfterTransactionCommit(message: Message) 15 | 16 | /** 17 | * Sends the given message after transaction commit of the current transaction. This method will raise 18 | * an exception if we are currently not inside a transaction 19 | */ 20 | fun sendAfterTransactionCommit(queue: String, message: Any) 21 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-jpa/src/main/kotlin/io/cloudflight/platform/spring/jpa/autoconfigure/TransactionProperties.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.jpa.autoconfigure 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties 4 | import org.springframework.transaction.support.AbstractPlatformTransactionManager 5 | 6 | @ConfigurationProperties("cloudflight.spring.tx") 7 | class TransactionProperties( 8 | 9 | /** 10 | * If true, participating transcation definitions will be checked if they match with the outside transaction. 11 | * This is especially important if if you open readOnly transaction and then would continue with non-read-only 12 | * transactions - you could unknowingly lose data then 13 | * 14 | * @see [AbstractPlatformTransactionManager.setValidateExistingTransaction] 15 | */ 16 | val validateExistingTransaction: Boolean = true 17 | ) 18 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-jpa/src/main/kotlin/io/cloudflight/platform/spring/jpa/autoconfigure/QueryDslAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.jpa.autoconfigure 2 | 3 | import com.querydsl.jpa.JPQLQueryFactory 4 | import com.querydsl.jpa.JPQLTemplates 5 | import com.querydsl.jpa.impl.JPAQueryFactory 6 | import jakarta.persistence.EntityManager 7 | import org.springframework.boot.autoconfigure.AutoConfiguration 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass 9 | import org.springframework.context.annotation.Bean 10 | 11 | @AutoConfiguration 12 | @ConditionalOnClass(value = [JPQLQueryFactory::class]) 13 | class QueryDslAutoConfiguration { 14 | 15 | @Bean 16 | fun jpaQueryFactory(entityManager: EntityManager): JPQLQueryFactory { 17 | // https://github.com/querydsl/querydsl/issues/3428 18 | return JPAQueryFactory(JPQLTemplates.DEFAULT, entityManager) 19 | } 20 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/test/kotlin/io/cloudflight/platform/spring/scheduling/noop/ApplicationTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.scheduling.noop 2 | 3 | import io.cloudflight.platform.spring.context.ApplicationContextProfiles.TEST 4 | import io.cloudflight.platform.spring.scheduling.lock.NoopLockProvider 5 | import net.javacrumbs.shedlock.core.LockProvider 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.junit.jupiter.api.Test 8 | import org.springframework.beans.factory.annotation.Autowired 9 | import org.springframework.boot.test.context.SpringBootTest 10 | import org.springframework.test.context.ActiveProfiles 11 | 12 | @ActiveProfiles(value = [TEST]) 13 | @SpringBootTest 14 | class ApplicationTest( 15 | @Autowired private val lockProvider: LockProvider 16 | ) { 17 | 18 | @Test 19 | fun `context starts`() { 20 | assertThat(lockProvider).isInstanceOf(NoopLockProvider::class.java) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/main/kotlin/io/cloudflight/platform/spring/storage/azure/service/AzuriteSasTokenStrategy.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.azure.service 2 | 3 | import com.azure.storage.blob.BlobClient 4 | import com.azure.storage.blob.sas.BlobContainerSasPermission 5 | import com.azure.storage.blob.sas.BlobServiceSasSignatureValues 6 | import java.time.Duration 7 | import java.time.OffsetDateTime 8 | 9 | internal class AzuriteSasTokenStrategy : SasTokenStrategy { 10 | 11 | override fun getSasToken( 12 | blobClient: BlobClient, 13 | validityDuration: Duration, 14 | permission: BlobContainerSasPermission? 15 | ): String { 16 | val accountSasValues = BlobServiceSasSignatureValues( 17 | OffsetDateTime.now().plus(validityDuration), 18 | permission 19 | ) 20 | return blobClient.generateSas(accountSasValues) 21 | } 22 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/MessageSourceUtils.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n 2 | 3 | import mu.KotlinLogging 4 | import org.springframework.context.MessageSource 5 | import org.springframework.context.MessageSourceResolvable 6 | import org.springframework.context.NoSuchMessageException 7 | import java.util.* 8 | 9 | private val LOG = KotlinLogging.logger { } 10 | 11 | fun MessageSource.safeGetMessage(code: String, locale: Locale): String { 12 | return this.safeGetMessage(MessageSourceResolvable { arrayOf(code) }, locale) 13 | } 14 | 15 | fun MessageSource.safeGetMessage(resolvable: MessageSourceResolvable, locale: Locale): String { 16 | try { 17 | return this.getMessage(resolvable, locale) 18 | } catch (e: NoSuchMessageException) { 19 | LOG.error(e) { e.message } 20 | return e.message ?: "No message found for code '${resolvable.codes?.last()}'" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-bdd/src/main/kotlin/io/cloudflight/platform/spring/test/jgiven/TicketDescriptionGenerator.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.jgiven 2 | 3 | import com.tngtech.jgiven.annotation.TagDescriptionGenerator 4 | import com.tngtech.jgiven.config.TagConfiguration 5 | import java.util.* 6 | 7 | internal class TicketDescriptionGenerator : TagDescriptionGenerator { 8 | 9 | private val serverUrl = System.getProperty(TICKET_SERVER_URL, UNKNOWN_SERVER_URL) 10 | 11 | override fun generateDescription( 12 | tagConfiguration: TagConfiguration?, 13 | annotation: Annotation?, 14 | value: Any? 15 | ): String { 16 | return String.format(Locale.ENGLISH, "$serverUrl", value, value); 17 | } 18 | 19 | companion object { 20 | private const val UNKNOWN_SERVER_URL = "https://jira.unknown.com/browse/%s" 21 | const val TICKET_SERVER_URL = "jgiven.issue.server.url" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-testcontainers/src/main/kotlin/io/cloudflight/platform/spring/test/testcontainers/azurite/AzuriteContainer.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.testcontainers.azurite 2 | 3 | import org.testcontainers.containers.GenericContainer 4 | import org.testcontainers.utility.DockerImageName 5 | 6 | class AzuriteContainer(dockerImageName: DockerImageName) : GenericContainer(dockerImageName) { 7 | 8 | constructor() : this(DockerImageName.parse(DEFAULT_IMAGE_NAME)) 9 | 10 | init { 11 | addExposedPort(AZURITE_PORT) 12 | } 13 | 14 | val port: Int = AZURITE_PORT 15 | 16 | val accountName = AzuriteContainerConnectionDetailsFactory.ACCOUNT_NAME 17 | val accountKey = AzuriteContainerConnectionDetailsFactory.ACCOUNT_KEY 18 | 19 | companion object { 20 | private const val DEFAULT_IMAGE_NAME = "mcr.microsoft.com/azure-storage/azurite:3.35.0" 21 | 22 | private const val AZURITE_PORT = 10000 23 | } 24 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/impl/I18nServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n.impl 2 | 3 | import io.cloudflight.platform.spring.i18n.I18nService 4 | import io.cloudflight.platform.spring.i18n.autoconfigure.I18nProperties 5 | import java.util.* 6 | 7 | internal class I18nServiceImpl(private val properties: I18nProperties) : I18nService { 8 | 9 | init { 10 | require(properties.locales.isNotEmpty()) { 11 | "at least one locale must be available, configured by 'cloudflight.i18n.locales'" 12 | } 13 | require(properties.primary == null || properties.locales.contains(properties.primary!!)) { 14 | "${properties.primary} is not part of 'cloudflight.i18n.locales': ${properties.locales}" 15 | } 16 | } 17 | 18 | override val availableLocales: List 19 | get() = properties.locales 20 | override val primaryLocale: Locale? 21 | get() = properties.primary 22 | } 23 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':platform-spring-bom:platform-spring-json') 3 | implementation project(':platform-spring-bom:platform-spring-logging') 4 | 5 | implementation 'com.fasterxml.jackson.core:jackson-databind' 6 | 7 | api('javax.cache:cache-api') 8 | 9 | implementation 'org.springframework.boot:spring-boot-starter-cache' 10 | implementation 'org.springframework.boot:spring-boot-starter-data-redis' 11 | 12 | implementation 'org.springframework.session:spring-session-data-redis' 13 | 14 | testImplementation project(':platform-spring-test-bom:platform-spring-test') 15 | testImplementation project(':platform-spring-test-bom:platform-spring-test-testcontainers') 16 | 17 | testImplementation 'org.springframework.boot:spring-boot-starter-security' 18 | testImplementation 'org.springframework.boot:spring-boot-starter-web' 19 | 20 | testImplementation project(':platform-spring-test-bom:platform-spring-test-testcontainers') 21 | } 22 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/autoconfigure/PlatformI18nWebAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.i18n.I18nService 4 | import io.cloudflight.platform.spring.i18n.ListResourceBundleMessageSource 5 | import io.cloudflight.platform.spring.i18n.controller.I18nController 6 | import org.springframework.boot.autoconfigure.AutoConfiguration 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty 8 | import org.springframework.context.annotation.Bean 9 | 10 | @AutoConfiguration 11 | @ConditionalOnProperty(prefix = "cloudflight.i18n.httpendpoint", name = ["enabled"], matchIfMissing = true) 12 | class PlatformI18nWebAutoConfiguration { 13 | 14 | @Bean 15 | fun i18nController( 16 | messageSource: ListResourceBundleMessageSource, 17 | i18nService: I18nService 18 | ): I18nController { 19 | return I18nController(messageSource, i18nService) 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation-api/src/main/kotlin/io/cloudflight/platform/spring/validation/api/ValidationOpenApiGenerator.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.api 2 | 3 | import io.cloudflight.platform.spring.validation.api.dto.ErrorResponse 4 | import io.swagger.annotations.Api 5 | import io.swagger.annotations.ApiOperation 6 | import io.swagger.annotations.ApiResponse 7 | import io.swagger.annotations.ApiResponses 8 | import org.springframework.web.bind.annotation.GetMapping 9 | 10 | @Api("OpenApiGenerator", description = "Dummy api used to generated objects to the openapi spec") 11 | interface ValidationOpenApiGenerator { 12 | 13 | @ApiOperation("Get an error response from the system") 14 | @GetMapping("/error") 15 | @ApiResponses( 16 | ApiResponse(code = 400, response = ErrorResponse::class, message = "Bad requests or validation exceptions"), 17 | ApiResponse(code = 500, response = ErrorResponse::class, message = "Internal server errors") 18 | ) 19 | fun getErrorResponse(): ErrorResponse 20 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/main/kotlin/io/cloudflight/platform/spring/caching/EvictCacheErrorHandler.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching 2 | 3 | import org.slf4j.LoggerFactory 4 | import org.springframework.cache.Cache 5 | import org.springframework.cache.interceptor.SimpleCacheErrorHandler 6 | import org.springframework.data.redis.serializer.SerializationException 7 | 8 | class EvictCacheErrorHandler : SimpleCacheErrorHandler() { 9 | override fun handleCacheGetError(exception: RuntimeException, cache: Cache, key: Any) { 10 | if (exception is SerializationException) { 11 | LOG.error("SerializationException during (de)serialization of value with key '$key' at cache ${cache.name} " + 12 | "with reason ${exception.mostSpecificCause.message}. '$key' is evicted from the cache.") 13 | cache.evict(key) 14 | } else { 15 | throw exception 16 | } 17 | } 18 | 19 | companion object { 20 | private val LOG = LoggerFactory.getLogger(EvictCacheErrorHandler::class.java) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':platform-spring-bom:platform-spring-json') 3 | implementation project(':platform-spring-bom:platform-spring-logging') 4 | 5 | testImplementation project(':platform-spring-test-bom:platform-spring-test') 6 | 7 | implementation 'com.fasterxml.jackson.core:jackson-databind' 8 | 9 | testRuntimeOnly 'com.h2database:h2' 10 | 11 | testImplementation 'org.awaitility:awaitility' 12 | 13 | api 'org.springframework.amqp:spring-rabbit' 14 | implementation 'org.springframework.boot:spring-boot-starter-amqp' 15 | testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa' 16 | testImplementation 'org.springframework.boot:spring-boot-test-autoconfigure' 17 | 18 | testImplementation project(':platform-spring-test-bom:platform-spring-test-testcontainers') 19 | 20 | testImplementation("org.testcontainers:rabbitmq") 21 | testImplementation("org.testcontainers:junit-jupiter") 22 | testImplementation("org.springframework.boot:spring-boot-testcontainers") 23 | } 24 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/java/io/cloudflight/platform/spring/logging/autoconfigure/LoggingServiceAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.autoconfigure; 2 | 3 | import io.cloudflight.platform.spring.logging.interceptor.LogParamInterceptor; 4 | import io.cloudflight.platform.spring.logging.service.LogFlusher; 5 | import org.springframework.boot.autoconfigure.AutoConfiguration; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | /** 10 | * {@link EnableAutoConfiguration Auto-configuration} for Logging Service 11 | *

12 | * It also ensures that all logs are sent when the application shuts down. 13 | * 14 | * @author Clemens Grabmann 15 | */ 16 | @AutoConfiguration 17 | public class LoggingServiceAutoConfiguration { 18 | 19 | @Bean 20 | public LogFlusher logFlusher() { 21 | return new LogFlusher(); 22 | } 23 | 24 | @Bean 25 | public LogParamInterceptor logParamInterceptor() { 26 | return new LogParamInterceptor(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/main/kotlin/io/cloudflight/platform/spring/server/autoconfigure/PlatformServerAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.server.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.server.ApplicationStartupPrinter 4 | import io.cloudflight.platform.spring.server.ServerModuleIdentification 5 | import org.springframework.boot.autoconfigure.AutoConfiguration 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean 7 | import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup 8 | import org.springframework.context.annotation.Bean 9 | import org.springframework.context.annotation.ComponentScan 10 | 11 | @AutoConfiguration 12 | @ComponentScan(basePackageClasses = [ServerModuleIdentification::class]) 13 | class PlatformServerAutoConfiguration { 14 | 15 | @Bean 16 | @ConditionalOnBean(value = [BufferingApplicationStartup::class]) 17 | internal fun startupPrinter(startup: BufferingApplicationStartup): ApplicationStartupPrinter { 18 | return ApplicationStartupPrinter(startup) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/main/kotlin/io/cloudflight/platform/spring/caching/serializer/SafeRedisSessionSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching.serializer 2 | 3 | import org.slf4j.LoggerFactory 4 | import org.springframework.data.redis.serializer.RedisSerializer 5 | import org.springframework.data.redis.serializer.SerializationException 6 | 7 | /** 8 | * @author Harald Radi (harald.radi@cloudflight.io) 9 | * @version 1.0 10 | */ 11 | class SafeRedisSessionSerializer( 12 | private val serializer: RedisSerializer 13 | ) : RedisSerializer { 14 | override fun serialize(t: T?): ByteArray? { 15 | return serializer.serialize(t) 16 | } 17 | 18 | override fun deserialize(bytes: ByteArray?): T? { 19 | return try { 20 | serializer.deserialize(bytes) 21 | } catch (e: SerializationException) { 22 | LOG.warn("non-deserializable session") 23 | null 24 | } 25 | } 26 | 27 | companion object { 28 | private val LOG = LoggerFactory.getLogger(SafeRedisSessionSerializer::class.java) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(":platform-spring-bom:platform-spring-cloud-storage:platform-spring-cloud-storage-api") 3 | implementation project(":platform-spring-bom:platform-spring-context") 4 | 5 | implementation platform(libs.azure.dependencies.bom) 6 | 7 | implementation 'com.azure.spring:spring-cloud-azure-starter-storage-blob' 8 | runtimeOnly 'com.azure.spring:spring-cloud-azure-starter-keyvault-secrets' 9 | 10 | implementation 'com.azure:azure-identity' 11 | 12 | testImplementation 'org.springframework.boot:spring-boot-starter-web' 13 | testImplementation project(":platform-spring-bom:platform-spring-json") 14 | testImplementation 'com.fasterxml.jackson.module:jackson-module-kotlin' 15 | 16 | testImplementation project(':platform-spring-test-bom:platform-spring-test-testcontainers') 17 | testImplementation("org.testcontainers:junit-jupiter") 18 | testImplementation("org.springframework.boot:spring-boot-testcontainers") 19 | testImplementation(libs.testcontainers.junit4.mock) 20 | 21 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/java/io/cloudflight/platform/spring/logging/annotation/LogParam.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * Annotation providing a convenient way to add the value of a method param to the log MDC information. 7 | */ 8 | @Target(ElementType.PARAMETER) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | @Repeatable(LogParams.class) 12 | public @interface LogParam { 13 | /** 14 | *

Defines the field of the annotated parameter that should be printed.

15 | *

If no field is defined the parameter value is printed as is.

16 | *

Example:

17 | *
    18 | *
  • field
  • 19 | *
  • field.subfield
  • 20 | *
21 | * 22 | * @return path to field 23 | */ 24 | String field() default ""; 25 | 26 | /** 27 | *

Defines the name with which the value is put into the log MDC information.

28 | *

If no name is defined the name used will be <paramName>[.<field>]

29 | * 30 | * @return name 31 | */ 32 | String name() default ""; 33 | } 34 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-context/src/main/kotlin/io/cloudflight/platform/spring/context/ApplicationContextProfiles.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.context 2 | 3 | /** 4 | * Use the constants in this class in conjunction with Spring's Environment profiles 5 | * 6 | * @see [org.springframework.context.annotation.Profile] 7 | * @see [org.springframework.test.context.ActiveProfiles] 8 | * @see [org.springframework.core.env.Environment] 9 | */ 10 | object ApplicationContextProfiles { 11 | 12 | /** 13 | * to be used for local development inside the IDE 14 | */ 15 | const val DEVELOPMENT = "development" 16 | 17 | /** 18 | * Staging environment 19 | */ 20 | const val STAGING = "staging" 21 | 22 | /** 23 | * Production environment 24 | */ 25 | const val PRODUCTION = "production" 26 | 27 | /** 28 | * default profile to be used in spring application tests 29 | */ 30 | const val TEST = "test" 31 | 32 | /** 33 | * To be used in test cases when using [org.testcontainers.Testcontainers], those tests run reasonabily slower 34 | * and it should be possible to not run them explicitely 35 | */ 36 | const val TEST_CONTAINER = "testcontainer" 37 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/kotlin/io/cloudflight/platform/spring/logging/mdc/impl/MDCScopeImpl.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.mdc.impl 2 | 3 | import io.cloudflight.platform.spring.logging.mdc.MDCAccess 4 | import io.cloudflight.platform.spring.logging.mdc.MDCScope 5 | 6 | private class MDCAccessImpl(private val mdcScope: MDCScopeImpl) : MDCAccess by mdcScope 7 | 8 | internal class MDCScopeImpl(private val ignoreNullValues: Boolean) : MDCScope, MDCAccess { 9 | private val addedMDCEntries = mutableSetOf() 10 | 11 | override val MDC: MDCAccess = MDCAccessImpl(this) 12 | 13 | override fun put(key: String, value: String?) { 14 | if (!(ignoreNullValues && value == null)) { 15 | addedMDCEntries.add(key) 16 | org.slf4j.MDC.put(key, value) 17 | } 18 | } 19 | 20 | override fun put(entry: Pair) { 21 | this.put(entry.first, entry.second) 22 | } 23 | 24 | override fun remove(key: String) { 25 | org.slf4j.MDC.remove(key) 26 | addedMDCEntries.remove(key) 27 | } 28 | 29 | fun removeAllAdded() { 30 | addedMDCEntries.forEach { 31 | org.slf4j.MDC.remove(it) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/main/kotlin/io/cloudflight/platform/spring/storage/azure/service/AzureSasTokenStrategy.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.azure.service 2 | 3 | import com.azure.storage.blob.BlobClient 4 | import com.azure.storage.blob.BlobServiceClient 5 | import com.azure.storage.blob.sas.BlobContainerSasPermission 6 | import com.azure.storage.blob.sas.BlobServiceSasSignatureValues 7 | import java.time.Duration 8 | import java.time.OffsetDateTime 9 | 10 | internal class AzureSasTokenStrategy(private val client: BlobServiceClient) : SasTokenStrategy { 11 | 12 | override fun getSasToken( 13 | blobClient: BlobClient, 14 | validityDuration: Duration, 15 | permission: BlobContainerSasPermission? 16 | ): String { 17 | val accountSasValues = BlobServiceSasSignatureValues( 18 | OffsetDateTime.now().plus(validityDuration), 19 | permission 20 | ) 21 | // TODO cache the user delegation key and extend validity to X? 22 | val userDelegationKey = client.getUserDelegationKey(null, OffsetDateTime.now().plus(validityDuration)) 23 | return blobClient.generateUserDelegationSas(accountSasValues, userDelegationKey) 24 | } 25 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/resources/io/cloudflight/platform/spring/logging/clf-base.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/main/kotlin/io/cloudflight/platform/spring/server/ServerModuleIdentification.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.server 2 | 3 | import java.time.Instant 4 | 5 | /** 6 | * Inject this interface into your Spring Beans in order to get information about the current server module 7 | */ 8 | interface ServerModuleIdentification { 9 | /** 10 | * the group id of your server module 11 | */ 12 | fun getGroup(): String 13 | 14 | /** 15 | * the name of your server module 16 | */ 17 | fun getName(): String 18 | 19 | /** 20 | * The unique ID of your server module. In case you have git.properties on your classpath, this is the recent git hash 21 | */ 22 | fun getId(): String 23 | 24 | /** 25 | * In case you have git.properties on your classpath, this is the recent git hash (shortened), otherwise it is null 26 | */ 27 | fun getIdShort(): String? 28 | 29 | /** 30 | * In case you have git.properties on your classpath, this is time of recent git commit, otherwise it is null 31 | */ 32 | fun getTime(): Instant? 33 | 34 | /** 35 | * The version of your server module as defined in your build system (i.e. Gradle) 36 | */ 37 | fun getVersion(): String 38 | } 39 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/ValidationException.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation 2 | 3 | import org.springframework.validation.BeanPropertyBindingResult 4 | import org.springframework.validation.BindException 5 | import org.springframework.validation.BindingResult 6 | 7 | /** 8 | * Provides an equivalent of [BindException] to transport validation failure to clients via [ValidationExceptionAdvice]. 9 | * 10 | * This is a safe alternative and can be used inside [org.springframework.transaction.annotation.Transactional] methods, 11 | * as it ensures any changes will be rolled back due to being subclassed from [RuntimeException]. 12 | */ 13 | class ValidationException private constructor(private val bindingResult: BindingResult) : RuntimeException(), 14 | BindingResult by bindingResult { 15 | constructor(target: Any, objectName: String) : this(BeanPropertyBindingResult(target, objectName)) 16 | 17 | override val message: String 18 | get() = bindingResult.toString() 19 | 20 | override fun equals(other: Any?): Boolean { 21 | return this === other || bindingResult.equals(other) 22 | } 23 | 24 | override fun hashCode(): Int { 25 | return bindingResult.hashCode() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/controller/I18nController.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n.controller 2 | 3 | import io.cloudflight.platform.spring.i18n.I18nService 4 | import io.cloudflight.platform.spring.i18n.ListResourceBundleMessageSource 5 | import io.cloudflight.platform.spring.i18n.LocaleInformation 6 | import org.springframework.context.i18n.LocaleContextHolder 7 | import org.springframework.web.bind.annotation.GetMapping 8 | import org.springframework.web.bind.annotation.PathVariable 9 | import org.springframework.web.bind.annotation.RequestMapping 10 | import org.springframework.web.bind.annotation.RestController 11 | import java.util.* 12 | 13 | @RestController 14 | @RequestMapping("/api/i18n") 15 | class I18nController(private val messageSource: ListResourceBundleMessageSource, 16 | private val i18nService: I18nService) { 17 | 18 | @GetMapping("/{locale}") 19 | fun getTranslations(@PathVariable() locale: Locale): Map { 20 | return messageSource.getAllMessages(locale) 21 | } 22 | 23 | @GetMapping("/localeInformation") 24 | fun getLocaleInformation(): LocaleInformation { 25 | return LocaleInformation(LocaleContextHolder.getLocale().language, i18nService.availableLocales.map { it.language }) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-tracing/src/main/kotlin/io/cloudflight/platform/spring/tracing/autoconfigure/PlatformTracingAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.tracing.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.tracing.filter.RequestLoggingDataProvider 4 | import io.cloudflight.platform.spring.tracing.filter.RequestLoggingFilter 5 | import org.springframework.boot.autoconfigure.AutoConfiguration 6 | import org.springframework.boot.autoconfigure.security.SecurityProperties 7 | import org.springframework.boot.web.servlet.FilterRegistrationBean 8 | import org.springframework.context.ApplicationContext 9 | import org.springframework.context.annotation.Bean 10 | 11 | @AutoConfiguration 12 | class PlatformTracingAutoConfiguration { 13 | 14 | @Bean 15 | fun requestLoggingFilter(applicationContext: ApplicationContext): FilterRegistrationBean { 16 | val registrationBean: FilterRegistrationBean = FilterRegistrationBean() 17 | registrationBean.filter = 18 | RequestLoggingFilter(applicationContext.getBeansOfType(RequestLoggingDataProvider::class.java).values) 19 | registrationBean.order = SecurityProperties.DEFAULT_FILTER_ORDER + 1 20 | registrationBean.addUrlPatterns( 21 | "/*" 22 | ) 23 | return registrationBean 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/github-build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | name: Build & Test 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Clone repository 16 | uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | - name: Set up Java 20 | uses: actions/setup-java@v3 21 | with: 22 | java-version: '17' 23 | distribution: 'temurin' 24 | - name: Validate Gradle wrapper 25 | uses: gradle/actions/wrapper-validation@v4 26 | - name : Retrieve module version from Reckon 27 | run: echo "VERSION_NAME=$(${{github.workspace}}/gradlew -q clfPrintVersion)" >> $GITHUB_OUTPUT 28 | id: retrieve_version 29 | - name: Publish module version to Github Step Summary 30 | run: | 31 | echo "# ${{steps.retrieve_version.outputs.VERSION_NAME}}" >> $GITHUB_STEP_SUMMARY 32 | - name: Build with Gradle 33 | uses: gradle/gradle-build-action@v2 34 | with: 35 | arguments: build 36 | - name: Publish Test Report 37 | uses: mikepenz/action-junit-report@v3 38 | if: always() # always run even if the previous step fails 39 | with: 40 | report_paths: '**/build/test-results/test/TEST-*.xml' 41 | include_passed: true 42 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/test/kotlin/io/cloudflight/platform/spring/scheduling/redis/ApplicationTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.scheduling.redis 2 | 3 | import io.cloudflight.platform.spring.context.ApplicationContextProfiles.TEST 4 | import io.cloudflight.platform.spring.test.testcontainers.redis.RedisContainer 5 | import net.javacrumbs.shedlock.core.LockProvider 6 | import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | import org.springframework.beans.factory.annotation.Autowired 10 | import org.springframework.boot.test.context.SpringBootTest 11 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection 12 | import org.springframework.test.context.ActiveProfiles 13 | import org.testcontainers.junit.jupiter.Container 14 | import org.testcontainers.junit.jupiter.Testcontainers 15 | 16 | @ActiveProfiles(value = [TEST]) 17 | @SpringBootTest 18 | @Testcontainers 19 | class ApplicationTest( 20 | @Autowired private val lockProvider: LockProvider 21 | ) { 22 | 23 | companion object { 24 | @ServiceConnection 25 | @Container 26 | val redis = RedisContainer() 27 | } 28 | 29 | 30 | @Test 31 | fun `context starts`() { 32 | assertThat(lockProvider).isInstanceOf(RedisLockProvider::class.java) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/impl/ErrorResponseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.impl 2 | 3 | import io.cloudflight.platform.spring.i18n.LocaleAccess 4 | import io.cloudflight.platform.spring.i18n.safeGetMessage 5 | import io.cloudflight.platform.spring.validation.ErrorResponseFactory 6 | import io.cloudflight.platform.spring.validation.api.dto.ErrorResponse 7 | import io.cloudflight.platform.spring.validation.api.dto.FieldMessageDto 8 | import io.cloudflight.platform.spring.validation.api.dto.GlobalMessageDto 9 | import io.cloudflight.platform.spring.validation.api.dto.MessageSeverity 10 | import org.springframework.context.MessageSource 11 | 12 | internal class ErrorResponseFactoryImpl(private val messageSource: MessageSource) : ErrorResponseFactory, LocaleAccess { 13 | 14 | override fun createFieldMessageResponse(field: String, code: String): ErrorResponse { 15 | val error = FieldMessageDto( 16 | field, 17 | GlobalMessageDto(messageSource.safeGetMessage(code, currentLocale), MessageSeverity.ERROR) 18 | ) 19 | return ErrorResponse(fieldMessages = listOf(error)) 20 | } 21 | 22 | override fun createGlobalMessageResponse(code: String): ErrorResponse { 23 | val error = GlobalMessageDto(messageSource.safeGetMessage(code, currentLocale), MessageSeverity.ERROR) 24 | return ErrorResponse(globalMessages = listOf(error)) 25 | } 26 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/test/kotlin/io/cloudflight/platform/spring/monitoring/ManagementSecurityIntegrationSpringSecurityTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.monitoring 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.autoconfigure.SpringBootApplication 5 | import org.springframework.boot.test.context.SpringBootTest 6 | import org.springframework.context.annotation.Bean 7 | import org.springframework.context.annotation.Configuration 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity 10 | import org.springframework.security.config.annotation.web.invoke 11 | import org.springframework.security.web.DefaultSecurityFilterChain 12 | 13 | @SpringBootTest(classes = [ManagementSecurityIntegrationSpringSecurityTest.TestApplication::class]) 14 | class ManagementSecurityIntegrationSpringSecurityTest { 15 | 16 | @Test 17 | fun contestStarts() { 18 | } 19 | 20 | @SpringBootApplication 21 | class TestApplication { 22 | 23 | @Configuration 24 | @EnableWebSecurity 25 | class SecurityConfiguration { 26 | 27 | @Bean 28 | fun customerFilter(http: HttpSecurity): DefaultSecurityFilterChain? { 29 | http { 30 | csrf { 31 | disable() 32 | } 33 | } 34 | return http.build() 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/test/kotlin/io/cloudflight/platform/spring/logging/interceptor/MySpringBean.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.interceptor 2 | 3 | import io.cloudflight.platform.spring.logging.annotation.LogParam 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.slf4j.MDC 6 | import org.springframework.stereotype.Service 7 | 8 | @Service 9 | class MySpringBean { 10 | 11 | fun sayHello(@LogParam name: String) { 12 | assertThat(MDC.get("name")).isEqualTo(name) 13 | } 14 | 15 | fun sayHelloWithNamedParameter(@LogParam(name = "myName") name: String) { 16 | assertThat(MDC.get("myName")).isEqualTo(name) 17 | } 18 | 19 | fun sayHelloWithField(@LogParam(field = "firstName") person: Person) { 20 | assertThat(MDC.get("person.firstName")).isEqualTo(person.firstName) 21 | } 22 | 23 | fun sayHelloWithFieldAndName(@LogParam(field = "firstName", name = "myFirstName") person: Person) { 24 | assertThat(MDC.get("myFirstName")).isEqualTo(person.firstName) 25 | } 26 | 27 | fun sayHelloWithMultipleFieldNames( 28 | @io.cloudflight.platform.spring.logging.annotation.LogParams( 29 | LogParam(field = "firstName"), 30 | LogParam(field = "lastName") 31 | ) person: Person 32 | ) { 33 | assertThat(MDC.get("person.firstName")).isEqualTo(person.firstName) 34 | assertThat(MDC.get("person.lastName")).isEqualTo(person.lastName) 35 | } 36 | 37 | data class Person(val firstName: String, val lastName: String) 38 | } 39 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/autoconfigure/PlatformValidationAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.validation.ErrorResponseFactory 4 | import io.cloudflight.platform.spring.validation.ErrorResponseMapper 5 | import io.cloudflight.platform.spring.validation.impl.ErrorResponseFactoryImpl 6 | import io.cloudflight.platform.spring.validation.impl.ErrorResponseMapperImpl 7 | import io.cloudflight.platform.spring.validation.ValidationExceptionAdvice 8 | import org.springframework.boot.autoconfigure.AutoConfiguration 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean 10 | import org.springframework.context.MessageSource 11 | import org.springframework.context.annotation.Bean 12 | 13 | @AutoConfiguration 14 | class PlatformValidationAutoConfiguration { 15 | 16 | @Bean 17 | @ConditionalOnMissingBean 18 | fun errorResponseFactory(messageSource: MessageSource): ErrorResponseFactory { 19 | return ErrorResponseFactoryImpl(messageSource) 20 | } 21 | 22 | @Bean 23 | @ConditionalOnMissingBean 24 | internal fun errorResponseMapper(messageSource: MessageSource): ErrorResponseMapper { 25 | return ErrorResponseMapperImpl(messageSource) 26 | } 27 | 28 | @Bean 29 | internal fun exceptionAdvice(errorResponseMapper: ErrorResponseMapper): ValidationExceptionAdvice { 30 | return ValidationExceptionAdvice(errorResponseMapper) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/test/kotlin/io/cloudflight/platform/spring/caching/RedisContainerIntegrationTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching 2 | 3 | import io.cloudflight.platform.spring.context.ApplicationContextProfiles 4 | import io.cloudflight.platform.spring.test.testcontainers.redis.RedisContainer 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.jupiter.api.Test 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.boot.test.context.SpringBootTest 9 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection 10 | import org.springframework.data.redis.core.RedisTemplate 11 | import org.springframework.test.context.ActiveProfiles 12 | import org.testcontainers.junit.jupiter.Container 13 | import org.testcontainers.junit.jupiter.Testcontainers 14 | import java.time.Duration 15 | 16 | @SpringBootTest(classes = [CachingTestApplication::class]) 17 | @ActiveProfiles(ApplicationContextProfiles.TEST_CONTAINER) 18 | @Testcontainers 19 | class RedisContainerIntegrationTest( 20 | @Autowired private val redisTemplate: RedisTemplate 21 | ) { 22 | 23 | companion object { 24 | @Container 25 | @ServiceConnection("redis") 26 | val redis = RedisContainer() 27 | } 28 | 29 | @Test 30 | fun contextStarts() { 31 | val opsForValue = redisTemplate.opsForValue() 32 | opsForValue.set("myKey", "myValue", Duration.ofSeconds(10)) 33 | assertThat(opsForValue.get("myKey")).isEqualTo("myValue") 34 | } 35 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/main/kotlin/io/cloudflight/platform/spring/messaging/impl/MessageBrokerServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging.impl 2 | 3 | import io.cloudflight.platform.spring.messaging.Message 4 | import io.cloudflight.platform.spring.messaging.MessageBrokerService 5 | import mu.KotlinLogging 6 | import org.springframework.amqp.rabbit.core.RabbitTemplate 7 | import org.springframework.transaction.support.TransactionSynchronization 8 | import org.springframework.transaction.support.TransactionSynchronizationManager 9 | 10 | internal class MessageBrokerServiceImpl(private val rabbitTemplate: RabbitTemplate) : MessageBrokerService { 11 | 12 | override fun sendAfterTransactionCommit(message: Message) { 13 | sendAfterTransactionCommit(message.queueName, message) 14 | } 15 | 16 | override fun sendAfterTransactionCommit(queue: String, message: Any) { 17 | TransactionSynchronizationManager.registerSynchronization(object : TransactionSynchronization { 18 | override fun afterCompletion(status: Int) { 19 | if (status == TransactionSynchronization.STATUS_COMMITTED) { 20 | LOG.debug("Sending message $message to queue $queue") 21 | rabbitTemplate.convertAndSend(queue, message) 22 | } else { 23 | LOG.error("Completion state is $status, message $message to queue $queue is not sent") 24 | } 25 | } 26 | }) 27 | } 28 | 29 | companion object { 30 | private val LOG = KotlinLogging.logger { } 31 | } 32 | } -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test/src/main/kotlin/io/cloudflight/platform/spring/test/openfeign/FeignTestClientFactory.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.openfeign 2 | 3 | import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration 4 | import org.springframework.cloud.openfeign.FeignAutoConfiguration 5 | import org.springframework.cloud.openfeign.FeignClientBuilder 6 | import org.springframework.cloud.openfeign.FeignClientsConfiguration 7 | import org.springframework.context.ApplicationContext 8 | import org.springframework.context.annotation.AnnotationConfigApplicationContext 9 | 10 | object FeignTestClientFactory { 11 | 12 | fun createClientApi( 13 | apiClass: Class, 14 | port: Int, 15 | path: String, 16 | clientContext: ApplicationContext = DEFAULT_CLIENT_CONTEXT 17 | ): T { 18 | return FeignClientBuilder(clientContext).forType(apiClass, apiClass.canonicalName) 19 | .url("http://localhost:$port/$path") 20 | .build() 21 | } 22 | 23 | fun createClientApi( 24 | apiClass: Class, 25 | port: Int, 26 | clientContext: ApplicationContext = DEFAULT_CLIENT_CONTEXT 27 | ): T { 28 | return createClientApi(apiClass, port,"", clientContext) 29 | } 30 | 31 | 32 | val DEFAULT_CLIENT_CONTEXT by lazy { 33 | AnnotationConfigApplicationContext( 34 | FeignAutoConfiguration::class.java, 35 | FeignClientsConfiguration::class.java, 36 | HttpMessageConvertersAutoConfiguration::class.java 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/impl/ErrorResponseMapperImpl.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.impl 2 | 3 | import io.cloudflight.platform.spring.i18n.LocaleAccess 4 | import io.cloudflight.platform.spring.i18n.safeGetMessage 5 | import io.cloudflight.platform.spring.validation.ErrorResponseMapper 6 | import io.cloudflight.platform.spring.validation.api.dto.ErrorResponse 7 | import io.cloudflight.platform.spring.validation.api.dto.FieldMessageDto 8 | import io.cloudflight.platform.spring.validation.api.dto.GlobalMessageDto 9 | import io.cloudflight.platform.spring.validation.api.dto.MessageSeverity 10 | import org.springframework.context.MessageSource 11 | import org.springframework.stereotype.Component 12 | import org.springframework.validation.BindingResult 13 | import org.springframework.validation.ObjectError 14 | 15 | @Component 16 | class ErrorResponseMapperImpl(private val messageSource: MessageSource) : LocaleAccess, ErrorResponseMapper { 17 | override fun mapBindingResult(bindingResult: BindingResult): ErrorResponse { 18 | val fieldMessages = bindingResult.fieldErrors 19 | .map { FieldMessageDto(it.field, mapMessage(it)) } 20 | val globalMessages = bindingResult.globalErrors 21 | .map(this::mapMessage) 22 | 23 | return ErrorResponse(fieldMessages, globalMessages) 24 | } 25 | 26 | private fun mapMessage(error: ObjectError) = GlobalMessageDto( 27 | message = messageSource.safeGetMessage(error, currentLocale), 28 | severity = MessageSeverity.ERROR 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /platform-spring-test-bom/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-platform' 3 | } 4 | 5 | javaPlatform { 6 | allowDependencies() 7 | } 8 | 9 | allprojects { 10 | configurations { 11 | api { 12 | exclude group: 'junit' 13 | exclude group: 'org.junit.vintage' 14 | } 15 | } 16 | } 17 | 18 | dependencies { 19 | // platform-spring-test-bom should inherit from platform-spring-bom (thus, also from spring boot) 20 | api platform(project(':platform-spring-bom')) 21 | api platform(libs.quickperf.bom) 22 | 23 | // with the following few lines we put some 3rd party libs automatically to the test-classpath of all 24 | // used projects. 25 | // why exactly those? because not only we think that they are useful and should be used 26 | // see https://phauer.com/2018/best-practices-unit-testing-kotlin/ 27 | api(libs.mockk) 28 | 29 | api(libs.assertj.core) 30 | 31 | api(libs.junit.api) 32 | api(libs.junit.params) 33 | api(libs.junit.engine) 34 | } 35 | 36 | subprojects { 37 | dependencies { 38 | // platform-spring-test-bom (and so all libraries from above) goes to the api classpath (implementation) 39 | implementation platform(project(':platform-spring-test-bom')) 40 | 41 | // https://stackoverflow.com/a/79650948 42 | testRuntimeOnly 'org.junit.platform:junit-platform-launcher' 43 | } 44 | } 45 | 46 | afterEvaluate { 47 | dependencies { 48 | constraints { 49 | for (Project p : project.subprojects) { 50 | api "$p.group:$p.name:$p.version" 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/test/kotlin/io/cloudflight/platform/spring/monitoring/ManagementSecurityIntegrationNoneSpringSecurityTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.monitoring 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.autoconfigure.SpringBootApplication 5 | import org.springframework.boot.test.context.SpringBootTest 6 | import org.springframework.context.annotation.Bean 7 | import org.springframework.context.annotation.Configuration 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity 10 | import org.springframework.security.config.annotation.web.invoke 11 | import org.springframework.security.web.DefaultSecurityFilterChain 12 | import org.springframework.test.context.ActiveProfiles 13 | 14 | @ActiveProfiles("none") 15 | @SpringBootTest(classes = [ManagementSecurityIntegrationNoneSpringSecurityTest.TestApplication::class]) 16 | class ManagementSecurityIntegrationNoneSpringSecurityTest { 17 | 18 | @Test 19 | fun contestStarts() { 20 | } 21 | 22 | @SpringBootApplication 23 | class TestApplication { 24 | 25 | @Configuration 26 | @EnableWebSecurity 27 | class SecurityConfiguration { 28 | 29 | @Bean 30 | fun customerFilter(http: HttpSecurity): DefaultSecurityFilterChain? { 31 | http { 32 | csrf { 33 | disable() 34 | } 35 | } 36 | return http.build() 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "io.cloudflight.autoconfigure-settings" version "1.1.2" 3 | } 4 | 5 | rootProject.name = 'cloudflight-platform-spring' 6 | 7 | reckon { 8 | scopeCalc = calcScopeFromCommitMessages() 9 | } 10 | 11 | include 'platform-spring-bom' 12 | include 'platform-spring-bom:platform-spring-caching' 13 | include 'platform-spring-bom:platform-spring-cloud-storage:platform-spring-cloud-storage-api' 14 | include 'platform-spring-bom:platform-spring-cloud-storage:platform-spring-cloud-storage-azure' 15 | include 'platform-spring-bom:platform-spring-context' 16 | include 'platform-spring-bom:platform-spring-i18n' 17 | include 'platform-spring-bom:platform-spring-jpa' 18 | include 'platform-spring-bom:platform-spring-json' 19 | include 'platform-spring-bom:platform-spring-logging' 20 | include 'platform-spring-bom:platform-spring-logging-server-config' 21 | include 'platform-spring-bom:platform-spring-messaging' 22 | include 'platform-spring-bom:platform-spring-monitoring' 23 | include 'platform-spring-bom:platform-spring-scheduling' 24 | include 'platform-spring-bom:platform-spring-server-config' 25 | include 'platform-spring-bom:platform-spring-tracing' 26 | include 'platform-spring-bom:platform-spring-validation' 27 | include 'platform-spring-bom:platform-spring-validation-api' 28 | 29 | include 'platform-spring-test-bom' 30 | include 'platform-spring-test-bom:platform-spring-test' 31 | include 'platform-spring-test-bom:platform-spring-test-archunit' 32 | include 'platform-spring-test-bom:platform-spring-test-bdd' 33 | include 'platform-spring-test-bom:platform-spring-test-jpa' 34 | include 'platform-spring-test-bom:platform-spring-test-testcontainers' 35 | 36 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test/src/main/kotlin/io/cloudflight/platform/spring/test/context/ApplicationStartupCustomizerFactory.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.context 2 | 3 | import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup 4 | import org.springframework.context.ConfigurableApplicationContext 5 | import org.springframework.test.context.ContextConfigurationAttributes 6 | import org.springframework.test.context.ContextCustomizer 7 | import org.springframework.test.context.ContextCustomizerFactory 8 | import org.springframework.test.context.MergedContextConfiguration 9 | 10 | /** 11 | * Thie [ContextCustomizerFactory] adds a [BufferingApplicationStartup] to the test context in order 12 | * to be able to tracker context startup in [SpringBootTest]s. 13 | */ 14 | class ApplicationStartupCustomizerFactory : ContextCustomizerFactory { 15 | override fun createContextCustomizer( 16 | testClass: Class<*>, 17 | configAttributes: MutableList 18 | ): ContextCustomizer { 19 | return TestContextCustomizer 20 | } 21 | 22 | private object TestContextCustomizer : ContextCustomizer { 23 | override fun customizeContext( 24 | context: ConfigurableApplicationContext, 25 | mergedConfig: MergedContextConfiguration 26 | ) { 27 | context.applicationStartup = BufferingApplicationStartup(2048) 28 | } 29 | 30 | override fun equals(other: Any?): Boolean { 31 | return other is TestContextCustomizer 32 | } 33 | 34 | override fun hashCode(): Int { 35 | return 1 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/main/kotlin/io/cloudflight/platform/spring/caching/autoconfigure/CachingConfigurerAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.caching.EvictCacheErrorHandler 4 | import org.springframework.beans.factory.config.BeanDefinition 5 | import org.springframework.boot.autoconfigure.AutoConfiguration 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass 7 | import org.springframework.cache.annotation.CachingConfigurer 8 | import org.springframework.cache.interceptor.CacheErrorHandler 9 | import org.springframework.context.annotation.Bean 10 | import org.springframework.context.annotation.Configuration 11 | import org.springframework.context.annotation.Import 12 | import org.springframework.context.annotation.Role 13 | import org.springframework.data.redis.core.RedisOperations 14 | 15 | @AutoConfiguration 16 | @ConditionalOnClass(RedisOperations::class) 17 | @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 18 | @Import(CachingConfigurerAutoConfiguration.CachingConfigurerConfiguration::class) 19 | class CachingConfigurerAutoConfiguration { 20 | @Configuration 21 | @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 22 | class CachingConfigurerConfiguration : CachingConfigurer { 23 | @Bean 24 | // https://github.com/spring-projects/spring-security/issues/14209#issuecomment-1836854767 25 | // @Role annotation does not propagate so it is required to be on all Beans in this file 26 | @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 27 | override fun errorHandler(): CacheErrorHandler { 28 | return EvictCacheErrorHandler() 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/main/kotlin/io/cloudflight/platform/spring/validation/ValidationExceptionAdvice.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation 2 | 3 | import io.cloudflight.platform.spring.validation.api.dto.ErrorResponse 4 | import org.springframework.core.annotation.Order 5 | import org.springframework.http.HttpStatus 6 | import org.springframework.http.ResponseEntity 7 | import org.springframework.validation.BindException 8 | import org.springframework.validation.BindingResult 9 | import org.springframework.web.bind.MethodArgumentNotValidException 10 | import org.springframework.web.bind.annotation.ControllerAdvice 11 | import org.springframework.web.bind.annotation.ExceptionHandler 12 | import org.springframework.web.bind.annotation.RestController 13 | 14 | @ControllerAdvice(annotations = [RestController::class]) 15 | @Order(ValidationConstants.VALIDATION_ADVICE_ORDER) 16 | class ValidationExceptionAdvice( 17 | private val errorResponseMapper: ErrorResponseMapper 18 | ) { 19 | 20 | @ExceptionHandler(BindException::class, ValidationException::class) 21 | fun bindFailed(exception: BindingResult): ResponseEntity { 22 | return mapBindingResults(exception) 23 | } 24 | 25 | @ExceptionHandler(MethodArgumentNotValidException::class) 26 | fun bindFailed(exception: MethodArgumentNotValidException): ResponseEntity { 27 | return mapBindingResults(exception.bindingResult) 28 | } 29 | 30 | private fun mapBindingResults(binding: BindingResult): ResponseEntity { 31 | return ResponseEntity 32 | .status(HttpStatus.BAD_REQUEST) 33 | .body(errorResponseMapper.mapBindingResult(binding)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-scheduling/src/main/kotlin/io/cloudflight/platform/spring/scheduling/autoconfigure/SchedulingAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.scheduling.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.caching.autoconfigure.CachingAutoConfiguration 4 | import io.cloudflight.platform.spring.scheduling.lock.NoopLockProvider 5 | import net.javacrumbs.shedlock.core.LockProvider 6 | import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider 7 | import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock 8 | import org.springframework.boot.autoconfigure.AutoConfiguration 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean 11 | import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 12 | import org.springframework.context.annotation.Bean 13 | import org.springframework.context.annotation.Configuration 14 | import org.springframework.context.annotation.Import 15 | import org.springframework.data.redis.connection.RedisConnectionFactory 16 | import org.springframework.scheduling.annotation.EnableScheduling 17 | 18 | @AutoConfiguration(after = [CachingAutoConfiguration::class, RedisAutoConfiguration::class]) 19 | @EnableScheduling 20 | @EnableSchedulerLock(defaultLockAtMostFor = "PT30S") 21 | @Import(value = [SchedulingAutoConfiguration.NoopConfiguration::class, SchedulingAutoConfiguration.RedisConfiguration::class]) 22 | class SchedulingAutoConfiguration { 23 | @Configuration 24 | @ConditionalOnBean(RedisConnectionFactory::class) 25 | class RedisConfiguration { 26 | @Bean 27 | fun lockProvider(connectionFactory: RedisConnectionFactory): LockProvider { 28 | return RedisLockProvider(connectionFactory) 29 | } 30 | } 31 | 32 | @Configuration 33 | @ConditionalOnMissingBean(RedisConnectionFactory::class) 34 | class NoopConfiguration { 35 | @Bean 36 | fun lockProvider(): LockProvider { 37 | return NoopLockProvider() 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/test/kotlin/io/cloudflight/platform/spring/caching/CachingConfigurerAutoConfigurationTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching 2 | 3 | import io.cloudflight.platform.spring.caching.autoconfigure.CachingConfigurerAutoConfiguration 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.Test 6 | import org.mockito.Mockito.mock 7 | import org.springframework.boot.autoconfigure.AutoConfigurations 8 | import org.springframework.boot.test.context.runner.ApplicationContextRunner 9 | import org.springframework.cache.CacheManager 10 | import org.springframework.cache.annotation.EnableCaching 11 | import org.springframework.cache.interceptor.CacheErrorHandler 12 | import org.springframework.context.annotation.Bean 13 | import org.springframework.context.annotation.Configuration 14 | import org.springframework.data.redis.cache.RedisCacheManager 15 | import org.springframework.data.redis.connection.RedisConnectionFactory 16 | 17 | 18 | class CachingConfigurerAutoConfigurationTest { 19 | 20 | private val contextRunner = ApplicationContextRunner() 21 | .withConfiguration(AutoConfigurations.of(CachingConfigurerAutoConfiguration::class.java)) 22 | 23 | @Test 24 | fun cacheErrorHandlerBean() { 25 | this.contextRunner.withUserConfiguration(BasicRedisConfiguration::class.java) 26 | .run { context -> 27 | assertThat(context).hasSingleBean(CacheErrorHandler::class.java) 28 | assertThat(context).getBean("errorHandler") 29 | .isSameAs(context.getBean(CacheErrorHandler::class.java)) 30 | } 31 | } 32 | 33 | @Configuration(proxyBeanMethods = false) 34 | @EnableCaching 35 | private class BasicRedisConfiguration { 36 | 37 | @Bean 38 | fun redisConnectionFactory(): RedisConnectionFactory { 39 | return mock(RedisConnectionFactory::class.java) 40 | } 41 | 42 | @Bean 43 | fun cacheManager(connectionFactory: RedisConnectionFactory): CacheManager { 44 | return RedisCacheManager.create(connectionFactory) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test-testcontainers/src/main/kotlin/io/cloudflight/platform/spring/test/testcontainers/azurite/AzuriteContainerConnectionDetailsFactory.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.testcontainers.azurite 2 | 3 | import io.cloudflight.platform.spring.autoconfigure.azurite.AzuriteConnectionDetails 4 | import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory 5 | import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource 6 | 7 | class AzuriteContainerConnectionDetailsFactory : 8 | ContainerConnectionDetailsFactory( 9 | "", 10 | "io.cloudflight.platform.spring.storage.azure.autoconfigure.PlatformAzureStorageBlobAutoConfiguration" 11 | ) { 12 | 13 | override fun getContainerConnectionDetails(source: ContainerConnectionSource): AzuriteConnectionDetails { 14 | return AzuriteContainerConnectionDetails(source) 15 | } 16 | 17 | private inner class AzuriteContainerConnectionDetails(source: ContainerConnectionSource) : 18 | ContainerConnectionDetails(source), AzuriteConnectionDetails { 19 | override val accountEndpoint: String 20 | get() = "http://" + container.host + ":" + container.getMappedPort( 21 | container.port 22 | ) + "/" + ACCOUNT_NAME 23 | override val accountName: String = ACCOUNT_NAME 24 | override val accountKey: String = ACCOUNT_KEY 25 | } 26 | 27 | companion object { 28 | /** 29 | * can't be changed, see [https://github.com/Azure/Azurite#default-storage-account](https://github.com/Azure/Azurite#default-storage-account) 30 | */ 31 | internal const val ACCOUNT_NAME = "devstoreaccount1" 32 | 33 | /** 34 | * can't be changed, see [https://github.com/Azure/Azurite#default-storage-account](https://github.com/Azure/Azurite#default-storage-account) 35 | */ 36 | internal const val ACCOUNT_KEY = 37 | "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" 38 | } 39 | } -------------------------------------------------------------------------------- /platform-spring-test-bom/platform-spring-test/src/test/kotlin/io/cloudflight/platform/spring/test/openfeign/FeignTestClientFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.test.openfeign 2 | 3 | import io.swagger.annotations.Api 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.Test 6 | import org.springframework.beans.factory.annotation.Autowired 7 | import org.springframework.boot.autoconfigure.SpringBootApplication 8 | import org.springframework.boot.test.context.SpringBootTest 9 | import org.springframework.boot.test.web.server.LocalServerPort 10 | import org.springframework.web.bind.annotation.GetMapping 11 | import org.springframework.web.bind.annotation.RequestParam 12 | import org.springframework.web.bind.annotation.RestController 13 | import java.time.LocalDateTime 14 | 15 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // <1> 16 | class FeignTestClientFactoryTest( 17 | @Autowired @LocalServerPort private val port: Int // <2> 18 | ) { 19 | 20 | private val helloApi = 21 | FeignTestClientFactory.createClientApi(HelloWorldApi::class.java, port) // <3> 22 | 23 | @Test 24 | fun helloWorld() { 25 | assertThat(helloApi.helloWorld("John").name).isEqualTo("John") 26 | } 27 | } 28 | 29 | // All subsequent classes usually come from the application itself, you don't need 30 | // them in your test classes. We just want to give an impression here of what we are 31 | // testing here 32 | 33 | @SpringBootApplication 34 | class TestApplication // <4> 35 | 36 | @Api("Project") 37 | interface HelloWorldApi { // <5> 38 | 39 | @GetMapping("/hello/world") 40 | fun helloWorld(@RequestParam("name") name: String): HelloWorldDto 41 | } 42 | 43 | data class HelloWorldDto(val name: String, val time: LocalDateTime) 44 | 45 | @RestController 46 | class HelloWorldController : HelloWorldApi { // <6> 47 | override fun helloWorld(name: String): HelloWorldDto { 48 | return HelloWorldDto(name, LocalDateTime.now()) 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/test/kotlin/io/cloudflight/platform/spring/logging/interceptor/LoggingApplicationTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.interceptor 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.AfterEach 5 | import org.junit.jupiter.api.BeforeEach 6 | import org.junit.jupiter.api.Test 7 | import org.junit.jupiter.api.extension.ExtendWith 8 | import org.slf4j.MDC 9 | import org.springframework.beans.factory.annotation.Autowired 10 | import org.springframework.boot.test.context.SpringBootTest 11 | import org.springframework.test.context.junit.jupiter.SpringExtension 12 | 13 | @ExtendWith(SpringExtension::class) 14 | @SpringBootTest(classes = [LoggingApplication::class]) 15 | class LoggingApplicationTest( 16 | @Autowired private val bean: MySpringBean, 17 | @Autowired private val bean2: BeanInterface 18 | ) { 19 | 20 | @BeforeEach 21 | @AfterEach 22 | fun `MDC is empty`() { 23 | assertThat(MDC.getCopyOfContextMap()).isNullOrEmpty() 24 | } 25 | 26 | @Test 27 | fun `LogParam works on a spring bean without interface (cglib proxy)`() { 28 | bean.sayHelloWithNamedParameter("hallo") 29 | } 30 | 31 | @Test 32 | fun `LogParam on implementation of an interface is considered as well`() { 33 | bean2.sayHelloWithNamedParameter("hello") 34 | } 35 | 36 | @Test 37 | fun `field name is being taken cglib proxies`() { 38 | bean.sayHello("hallo") 39 | } 40 | 41 | @Test 42 | fun `field name is being taken aop proxies`() { 43 | bean2.sayHello("hallo") 44 | } 45 | 46 | @Test 47 | fun `field is being evaluated as SPEL`() { 48 | bean.sayHelloWithField(MySpringBean.Person(firstName = "John", lastName = "Doe")) 49 | } 50 | 51 | @Test 52 | fun `field is being evaluated as SPEL, name is overridden`() { 53 | bean.sayHelloWithFieldAndName(MySpringBean.Person(firstName = "John", lastName = "Doe")) 54 | } 55 | 56 | @Test 57 | fun `multiple LogParams can be used as well`() { 58 | bean.sayHelloWithMultipleFieldNames(MySpringBean.Person(firstName = "John", lastName = "Doe")) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/autoconfigure/PlatformI18nAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.i18n.I18nService 4 | import io.cloudflight.platform.spring.i18n.impl.I18nServiceImpl 5 | import io.cloudflight.platform.spring.i18n.impl.PlatformMessageSourceImpl 6 | import org.springframework.boot.autoconfigure.AutoConfiguration 7 | import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration 8 | import org.springframework.boot.autoconfigure.context.MessageSourceProperties 9 | import org.springframework.boot.context.properties.EnableConfigurationProperties 10 | import org.springframework.context.MessageSource 11 | import org.springframework.context.annotation.Bean 12 | import org.springframework.util.StringUtils 13 | 14 | @AutoConfiguration(before = [MessageSourceAutoConfiguration::class]) 15 | @EnableConfigurationProperties(value = [I18nProperties::class]) 16 | class PlatformI18nAutoConfiguration : MessageSourceAutoConfiguration() { 17 | 18 | @Bean 19 | override fun messageSource(properties: MessageSourceProperties): MessageSource { 20 | val messageSource = PlatformMessageSourceImpl() 21 | if (properties.basename.isNotEmpty()) { 22 | messageSource.setBasenames(*properties.basename.toTypedArray()) 23 | } 24 | if (properties.encoding != null) { 25 | messageSource.setDefaultEncoding(properties.encoding.name()) 26 | } 27 | messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale) 28 | val cacheDuration = properties.cacheDuration 29 | if (cacheDuration != null) { 30 | messageSource.setCacheMillis(cacheDuration.toMillis()) 31 | } 32 | messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat) 33 | messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage) 34 | 35 | return messageSource 36 | } 37 | 38 | @Bean("platformI18nService") 39 | fun i18nService(i18nProperties: I18nProperties): I18nService { 40 | return I18nServiceImpl(i18nProperties) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/test/java/io/cloudflight/platform/spring/logging/LoggingJsonSplitterTest.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging; 2 | 3 | import ch.qos.logback.classic.LoggerContext; 4 | import ch.qos.logback.classic.util.ContextInitializer; 5 | import ch.qos.logback.core.joran.spi.JoranException; 6 | import org.junit.jupiter.api.AfterEach; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.slf4j.Logger; 11 | import org.slf4j.Marker; 12 | import org.slf4j.LoggerFactory; 13 | import org.slf4j.helpers.BasicMarkerFactory; 14 | 15 | public class LoggingJsonSplitterTest { 16 | private static final Logger LOG = LoggerFactory.getLogger(LoggingJsonSplitterTest.class); 17 | private final String originalLogbackFile = System.getProperty("logback.configurationFile"); 18 | private static final Marker TEST_MARKER = new BasicMarkerFactory().getMarker("TEST_MARKER"); 19 | 20 | @BeforeEach 21 | void setUp() { 22 | System.setProperty("logback.configurationFile", "src/test/resources/logback-test-json-splitter.xml"); 23 | configureLogback(); 24 | } 25 | 26 | @AfterEach 27 | void tearDown() { 28 | if (originalLogbackFile != null) { 29 | System.setProperty("logback.configurationFile", originalLogbackFile); 30 | } else { 31 | System.clearProperty("logback.configurationFile"); 32 | } 33 | configureLogback(); 34 | } 35 | 36 | private void configureLogback() { 37 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 38 | loggerContext.reset(); 39 | try { 40 | new ContextInitializer(loggerContext).autoConfig(); 41 | } catch (JoranException e) { 42 | throw new RuntimeException(e); 43 | } 44 | } 45 | 46 | @Test 47 | void logWithCloudbackConfig() { 48 | LOG.info("Hello World"); 49 | } 50 | 51 | @Test 52 | void logLongMessageWithJsonSplitterDoesNotFail() { 53 | Assertions.assertDoesNotThrow(() -> LOG.info(TEST_MARKER, "This is a long message. ".repeat(1000))); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/main/resources/io/cloudflight/platform/spring/logging/clf-json-appender.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | UTC 13 | 14 | true 15 | false 16 | 17 | 30 18 | 19 | 20 | true 21 | true 22 | 50 23 | 30 24 | 26 | 10000 27 | 28 | 29 | com\.sun\..* 30 | java\.lang\.reflect\.Method\.invoke 31 | net\.sf\.cglib\.proxy\.MethodProxy\.invoke 32 | org\.springframework\.aop\..* 33 | org\.springframework\.cglib\..* 34 | org\.springframework\.validation\..* 35 | sun\..* 36 | 37 | 38 | 39 | 40 | 41 | 42 | 1500 43 | seq 44 | 45 | 46 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/test/kotlin/io/cloudflight/platform/spring/monitoring/autoconfigure/ManagementServerPortEnvironmentPostProcessorTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.monitoring.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.monitoring.autoconfigure.ManagementServerPortEnvironmentPostProcessor.Companion.MANAGEMENT_SERVER_PORT_NAME 4 | import io.cloudflight.platform.spring.monitoring.autoconfigure.ManagementServerPortEnvironmentPostProcessor.Companion.SERVER_PORT_NAME 5 | import io.mockk.* 6 | import org.junit.jupiter.api.Test 7 | import org.springframework.boot.SpringApplication 8 | import org.springframework.core.env.ConfigurableEnvironment 9 | import org.springframework.core.env.MutablePropertySources 10 | import org.springframework.core.env.Profiles 11 | 12 | class ManagementServerPortEnvironmentPostProcessorTest { 13 | 14 | @Test 15 | fun noExplicitManagementServerPortOverride() { 16 | val env = mockk() 17 | 18 | every { env.acceptsProfiles(any() as Profiles) }.returns(false) 19 | every { env.getProperty(SERVER_PORT_NAME) }.returns("1024") 20 | every { env.getProperty(MANAGEMENT_SERVER_PORT_NAME) }.returns("1025") 21 | val propertySources=mockk() 22 | every { env.propertySources }.returns(propertySources) 23 | 24 | ManagementServerPortEnvironmentPostProcessor().postProcessEnvironment(env, SpringApplication()) 25 | 26 | verify (exactly = 0){ propertySources.addFirst(any()) } 27 | } 28 | 29 | @Test 30 | fun deriveManagementServerPortFromServerPort() { 31 | val env = mockk(relaxed = true) 32 | 33 | every { env.acceptsProfiles(any() as Profiles) }.returns(false) 34 | every { env.getProperty(SERVER_PORT_NAME) }.returns("1024") 35 | every { env.getProperty(MANAGEMENT_SERVER_PORT_NAME) }.returns(null) 36 | val propertySources=mockk() 37 | every { env.propertySources }.returns(propertySources) 38 | justRun { propertySources.addFirst(any()) } 39 | 40 | ManagementServerPortEnvironmentPostProcessor().postProcessEnvironment(env, SpringApplication()) 41 | 42 | verify (exactly = 1){ propertySources.addFirst(any()) } 43 | } 44 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/main/kotlin/io/cloudflight/platform/spring/caching/autoconfigure/SessionAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.caching.serializer.SafeRedisSessionSerializer 4 | import org.springframework.boot.autoconfigure.AutoConfiguration 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication 9 | import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 10 | import org.springframework.context.annotation.Bean 11 | import org.springframework.context.annotation.Configuration 12 | import org.springframework.context.annotation.Import 13 | import org.springframework.data.redis.connection.RedisConnectionFactory 14 | import org.springframework.data.redis.core.RedisOperations 15 | import org.springframework.data.redis.serializer.RedisSerializer 16 | import org.springframework.session.Session 17 | import org.springframework.session.SessionRepository 18 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession 19 | 20 | @AutoConfiguration( 21 | after = [CachingAutoConfiguration::class, RedisAutoConfiguration::class], 22 | before = [org.springframework.boot.autoconfigure.session.SessionAutoConfiguration::class] 23 | ) 24 | @ConditionalOnClass(Session::class) 25 | @ConditionalOnWebApplication 26 | @Import(SessionAutoConfiguration.RedisSessionConfig::class) 27 | class SessionAutoConfiguration { 28 | 29 | @Configuration 30 | @ConditionalOnClass(RedisOperations::class) 31 | @ConditionalOnBean(RedisConnectionFactory::class) 32 | @ConditionalOnMissingBean(SessionRepository::class) 33 | @EnableRedisHttpSession 34 | class RedisSessionConfig 35 | 36 | @Bean 37 | @ConditionalOnClass(RedisOperations::class) 38 | @ConditionalOnBean(SessionRepository::class) 39 | @ConditionalOnMissingBean(RedisSerializer::class) 40 | fun springSessionDefaultRedisSerializer(): RedisSerializer<*> { 41 | return SafeRedisSessionSerializer(RedisSerializer.java()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /platform-spring-bom/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-platform' 3 | } 4 | 5 | javaPlatform { 6 | allowDependencies() 7 | } 8 | 9 | allprojects { 10 | configurations { 11 | testImplementation { 12 | exclude group: 'junit' 13 | exclude group: 'org.junit.vintage' 14 | } 15 | } 16 | } 17 | 18 | dependencies { 19 | api platform(libs.opentelemetry.bom) 20 | api platform(libs.spring.boot.bom) 21 | api platform(libs.spring.cloud.bom) 22 | 23 | // we also need to add the kotlin-bom here as the Spring-Boot-BOM imports its own version of the kotlin-bom 24 | // and we want to override that with our version 25 | def kotlinVersion = project.rootProject.extensions.getByType(io.cloudflight.gradle.autoconfigure.AutoConfigureExtension).kotlin.kotlinVersion.get() 26 | api platform("org.jetbrains.kotlin:kotlin-bom:" + kotlinVersion) 27 | 28 | constraints { 29 | // we provide the latest version of guava here, as queryDSL pulls in a very old (and vulnerable) version of guava, and we want to override that here 30 | api(libs.guava) 31 | 32 | api(libs.springfox.swagger2) 33 | api(libs.swagger.annotations2) 34 | api(libs.swagger.annotations3) 35 | 36 | api(libs.shedlock.spring) 37 | api(libs.shedlock.provider.redis.spring) 38 | 39 | api(libs.jackson.databind.nullable) 40 | } 41 | } 42 | 43 | subprojects { p -> 44 | if (!p.name.equals("platform-spring-cloud-storage")) { 45 | dependencies { 46 | implementation platform(project(':platform-spring-bom')) 47 | testImplementation platform(project(':platform-spring-test-bom')) 48 | 49 | testImplementation project(':platform-spring-test-bom:platform-spring-test-bdd') 50 | 51 | // https://stackoverflow.com/a/79650948 52 | testRuntimeOnly 'org.junit.platform:junit-platform-launcher' 53 | 54 | if (p.plugins.hasPlugin(org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper)) { 55 | kapt platform(project(':platform-spring-bom')) 56 | } 57 | } 58 | } 59 | } 60 | 61 | afterEvaluate { 62 | dependencies { 63 | constraints { 64 | for (Project p : project.subprojects) { 65 | api "$p.group:$p.name:$p.version" 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/test/kotlin/io/cloudflight/platform/spring/logging/mdc/MdcScopeFunctionTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.mdc 2 | 3 | import org.assertj.core.api.Assertions 4 | import org.junit.jupiter.api.AfterEach 5 | import org.junit.jupiter.api.BeforeEach 6 | import org.junit.jupiter.api.Test 7 | import org.junit.jupiter.api.assertThrows 8 | import org.slf4j.MDC 9 | 10 | class MdcScopeFunctionTest { 11 | private val realMDC = MDC.getMDCAdapter() 12 | private val key = "normalKey" 13 | private val value = "normalValue" 14 | 15 | @BeforeEach 16 | @AfterEach 17 | fun `MDC is empty`() { 18 | Assertions.assertThat(MDC.getCopyOfContextMap()).isNullOrEmpty() 19 | } 20 | 21 | @Test 22 | fun `MDC can be set inside scope`() { 23 | val pair = "pairKey" to "pairValue" 24 | val expected = 10 25 | 26 | val actual = mdcScope { 27 | MDC.put(key, value) 28 | MDC.put(pair) 29 | 30 | Assertions.assertThat(realMDC.get(key)).isEqualTo(value) 31 | Assertions.assertThat(realMDC.get(pair.first)).isEqualTo(pair.second) 32 | expected 33 | } 34 | 35 | Assertions.assertThat(actual).isEqualTo(expected) 36 | } 37 | 38 | @Test 39 | fun `MDC value can be set to 'null'`() = mdcScope { 40 | MDC.put(key, null) 41 | 42 | Assertions.assertThat(realMDC.copyOfContextMap.keys).containsExactlyInAnyOrder(key) 43 | Assertions.assertThat(realMDC.get(key)).isNull() 44 | } 45 | 46 | @Test 47 | fun `MDC value is ignored if value is 'null'`() = mdcScope(ignoreNullValues = true) { 48 | MDC.put(key, null) 49 | 50 | Assertions.assertThat(realMDC.copyOfContextMap).isEmpty() 51 | } 52 | 53 | @Test 54 | fun `MDC can be removed inside scope`() = mdcScope { 55 | MDC.put(key to value) 56 | 57 | Assertions.assertThat(realMDC.get(key)).isEqualTo(value) 58 | 59 | MDC.remove(key) 60 | 61 | Assertions.assertThat(realMDC.get(key)).isNull() 62 | } 63 | 64 | @Test 65 | fun `MDC gets cleaned up when exception is thrown`() { 66 | assertThrows { 67 | mdcScope { 68 | MDC.put(key to value) 69 | throw IllegalArgumentException() 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/main/kotlin/io/cloudflight/platform/spring/messaging/autoconfigure/PlatformMessagingAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging.autoconfigure 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import io.cloudflight.platform.spring.messaging.MessageBrokerService 5 | import io.cloudflight.platform.spring.messaging.ProcessControlRegistry 6 | import io.cloudflight.platform.spring.messaging.impl.MessageBrokerServiceImpl 7 | import io.cloudflight.platform.spring.messaging.impl.RabbitListenerControlService 8 | import org.springframework.amqp.rabbit.core.RabbitTemplate 9 | import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry 10 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter 11 | import org.springframework.beans.factory.annotation.Autowired 12 | import org.springframework.boot.ApplicationRunner 13 | import org.springframework.boot.autoconfigure.AutoConfiguration 14 | import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration 15 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean 16 | import org.springframework.context.annotation.Bean 17 | 18 | /** 19 | * AutoConfiguration which adds support for asynchronous messaging via Spring Boot's AMQP support. 20 | * This configuration registers a [MessageBrokerService] which servers as gateway for all clients 21 | * to send messages immediately or after transaction commit. 22 | * 23 | * @author Klaus Lehner 24 | */ 25 | @AutoConfiguration(after = [RabbitAutoConfiguration::class]) 26 | class PlatformMessagingAutoConfiguration { 27 | 28 | @Autowired(required = false) 29 | var processControlRegistry: ProcessControlRegistry? = null 30 | 31 | @Bean 32 | @ConditionalOnBean(value = [RabbitListenerEndpointRegistry::class]) 33 | fun rabbitListenerStarter(registry: RabbitListenerEndpointRegistry): ApplicationRunner { 34 | return RabbitListenerControlService(registry, processControlRegistry) 35 | } 36 | 37 | @Bean 38 | fun producerJackson2MessageConverter(objectMapper: ObjectMapper): Jackson2JsonMessageConverter { 39 | return Jackson2JsonMessageConverter(objectMapper) 40 | } 41 | 42 | @Bean 43 | fun messageBrokerService(rabbitTemplate: RabbitTemplate): MessageBrokerService { 44 | return MessageBrokerServiceImpl(rabbitTemplate) 45 | } 46 | } -------------------------------------------------------------------------------- /.github/workflows/github-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Maven Central 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | stage: 7 | description: 'the stage of the version' 8 | required: true 9 | default: 'rc' 10 | type: choice 11 | options: 12 | - rc 13 | - final 14 | 15 | jobs: 16 | publish: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Clone repository 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - name: Set up Java 24 | uses: actions/setup-java@v3 25 | with: 26 | java-version: '17' 27 | distribution: 'temurin' 28 | - name: Validate Gradle wrapper 29 | uses: gradle/actions/wrapper-validation@v4 30 | - name : Retrieve module version from Reckon 31 | run: echo "VERSION_NAME=$(${{github.workspace}}/gradlew -q clfPrintVersion -Preckon.stage=${{ inputs.stage }})" >> $GITHUB_OUTPUT 32 | id: retrieve_version 33 | - name: Publish module version to Github Step Summary 34 | run: | 35 | echo "# ${{steps.retrieve_version.outputs.VERSION_NAME}}" >> $GITHUB_STEP_SUMMARY 36 | - name: Publish and Create Tag 37 | uses: gradle/gradle-build-action@v2 38 | with: 39 | arguments: check reckonTagPush publishToSonatype closeAndReleaseSonatypeStagingRepository -Preckon.stage=${{ inputs.stage }} 40 | env: 41 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 42 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 43 | ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.OSSRH_USERNAME }} 44 | ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.OSSRH_TOKEN }} 45 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 46 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 47 | - name: Publish Test Report 48 | uses: mikepenz/action-junit-report@v3 49 | if: always() # always run even if the previous step fails 50 | with: 51 | report_paths: '**/build/test-results/test/TEST-*.xml' 52 | include_passed: true 53 | - name: Create Release on GitHub 54 | uses: softprops/action-gh-release@v1 55 | with: 56 | name: ${{steps.retrieve_version.outputs.VERSION_NAME}} 57 | tag_name: ${{steps.retrieve_version.outputs.VERSION_NAME}} 58 | generate_release_notes: true 59 | append_body: true 60 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/main/kotlin/io/cloudflight/platform/spring/messaging/impl/RabbitListenerControlService.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging.impl 2 | 3 | import io.cloudflight.platform.spring.messaging.ProcessControlEvent 4 | import io.cloudflight.platform.spring.messaging.ProcessControlRegistry 5 | import org.slf4j.LoggerFactory 6 | import org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer 7 | import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry 8 | import org.springframework.boot.ApplicationArguments 9 | import org.springframework.boot.ApplicationRunner 10 | import org.springframework.context.ApplicationListener 11 | import org.springframework.core.task.SimpleAsyncTaskExecutor 12 | 13 | internal class RabbitListenerControlService( 14 | private val registry: RabbitListenerEndpointRegistry, 15 | private val processControlRegistry: ProcessControlRegistry? 16 | ) : ApplicationRunner, ApplicationListener { 17 | 18 | override fun run(args: ApplicationArguments?) { 19 | registry.listenerContainers.stream() 20 | .map { c -> c as AbstractMessageListenerContainer } 21 | .forEach { c -> c.setTaskExecutor(SimpleAsyncTaskExecutor(c.listenerId + "-")) } 22 | 23 | for (listenerContainer in registry.listenerContainers.map { c -> c as AbstractMessageListenerContainer }) { 24 | if (processControlRegistry == null || processControlRegistry.actionEnabled(ACTION_PREFIX + listenerContainer.listenerId)) { 25 | listenerContainer.start() 26 | } else { 27 | LOG.warn("Do not start ${listenerContainer.listenerId} as processing action is disabled") 28 | } 29 | } 30 | } 31 | 32 | override fun onApplicationEvent(event: ProcessControlEvent) { 33 | if (event.actionId.startsWith(ACTION_PREFIX)) { 34 | val listenerId = event.actionId.substringAfter(ACTION_PREFIX) 35 | val container = registry.getListenerContainer(listenerId) 36 | if (event.enabled) { 37 | LOG.info("Starting $listenerId") 38 | container.start() 39 | } else { 40 | LOG.warn("Stopping $listenerId") 41 | container.stop() 42 | } 43 | } 44 | } 45 | 46 | companion object { 47 | private const val ACTION_PREFIX = "rabbit-" 48 | private val LOG = LoggerFactory.getLogger(RabbitListenerControlService::class.java) 49 | } 50 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/test/kotlin/io/cloudflight/platform/spring/messaging/MessagingTestApplication.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging 2 | 3 | import org.slf4j.LoggerFactory 4 | import org.springframework.amqp.core.Queue 5 | import org.springframework.amqp.rabbit.annotation.RabbitListener 6 | import org.springframework.boot.autoconfigure.SpringBootApplication 7 | import org.springframework.context.annotation.Bean 8 | import org.springframework.context.annotation.Configuration 9 | import org.springframework.stereotype.Component 10 | import org.springframework.stereotype.Service 11 | import org.springframework.transaction.annotation.Transactional 12 | 13 | @SpringBootApplication 14 | class MessagingTestApplication 15 | 16 | @Configuration 17 | class MessagingConfiguration { 18 | 19 | @Bean 20 | fun myQueue(): Queue { 21 | return Queue("myQueue") 22 | } 23 | } 24 | 25 | data class MyMessage(val value1: String, val value2: String) : Message { 26 | override val queueName: String 27 | get() = "myQueue" 28 | } 29 | 30 | interface TransactionalService { 31 | fun performAction(value1: String, value2: String) 32 | 33 | fun lastActionPerformed(): Long? 34 | } 35 | 36 | @Service 37 | class TransactionalServiceImpl(private val messageBrokerService: MessageBrokerService) : TransactionalService { 38 | 39 | private var lastActionPerformed: Long? = null 40 | 41 | @Transactional 42 | override fun performAction(value1: String, value2: String) { 43 | lastActionPerformed = System.currentTimeMillis() 44 | Thread.sleep(100) 45 | messageBrokerService.sendAfterTransactionCommit(MyMessage(value1, value2)) 46 | if (value2 == "throw") { 47 | throw IllegalArgumentException("something happened") 48 | } 49 | } 50 | 51 | override fun lastActionPerformed(): Long? { 52 | return lastActionPerformed 53 | } 54 | } 55 | 56 | @Component 57 | class MyListener { 58 | 59 | var lastMessageReceivedThreadName: String? = null 60 | var lastMessageReceived: MyMessage? = null 61 | var lastMessageReceivedTime: Long? = null 62 | 63 | @RabbitListener(id = "myListener", queues = ["myQueue"]) 64 | fun messageReceived(message: MyMessage) { 65 | LOG.info("Received $message") 66 | lastMessageReceived = message 67 | lastMessageReceivedTime = System.currentTimeMillis() 68 | lastMessageReceivedThreadName = Thread.currentThread().name 69 | } 70 | 71 | companion object { 72 | val LOG = LoggerFactory.getLogger(MyListener::class.java) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/main/kotlin/io/cloudflight/platform/spring/server/ApplicationStartupPrinter.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.server 2 | 3 | import org.slf4j.LoggerFactory 4 | import org.springframework.boot.context.event.ApplicationStartedEvent 5 | import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup 6 | import org.springframework.boot.context.metrics.buffering.StartupTimeline 7 | import org.springframework.context.ApplicationListener 8 | 9 | /** 10 | * Internal class utilizing Spring Boot's [BufferingApplicationStartup] functionality to print 11 | * statistics of 12 | * 13 | * Set the logger `io.cloudflight.platform.server` to `TRACE` in your `logback-spring.xml` in order to have 14 | * the startup statistics in the log 15 | */ 16 | internal class ApplicationStartupPrinter(private val startup: BufferingApplicationStartup) : 17 | ApplicationListener { 18 | 19 | override fun onApplicationEvent(event: ApplicationStartedEvent) { 20 | if (LOG.isTraceEnabled) { 21 | printStartup() 22 | } 23 | } 24 | 25 | private fun printStartup() { 26 | val timeline = startup.bufferedTimeline 27 | 28 | val buffer = StringBuffer("Server startup summary: " + System.lineSeparator()) 29 | buffer.append("-".repeat(HEADER_WIDTH)).append(System.lineSeparator()) 30 | buffer.append("ms".padStart(TIME_COLUMN_WIDTH)) 31 | buffer.append(" ") 32 | buffer.append("Step").append(System.lineSeparator()) 33 | buffer.append("-".repeat(HEADER_WIDTH)).append(System.lineSeparator()) 34 | 35 | timeline.events.filter { it.startupStep.parentId == null || it.startupStep.parentId == 0.toLong() } 36 | .sortedBy { it.startupStep.id }.forEach { 37 | printEvent(buffer, it, 0, timeline) 38 | } 39 | 40 | LOG.trace(buffer.toString()) 41 | } 42 | 43 | private fun printEvent( 44 | buffer: StringBuffer, 45 | event: StartupTimeline.TimelineEvent, 46 | indent: Int, 47 | timeline: StartupTimeline 48 | ) { 49 | buffer.append(event.duration.toMillis().toString().padStart(TIME_COLUMN_WIDTH)) 50 | buffer.append(" ".repeat(2 + (indent * INDENT))) 51 | buffer.append(event.startupStep.name).append("(") 52 | buffer.append(event.startupStep.tags.joinToString(transform = { tag -> tag.key + "=" + tag.value })) 53 | buffer.append(")") 54 | buffer.append(System.lineSeparator()) 55 | timeline.events.filter { it.startupStep.parentId == event.startupStep.id }.sortedBy { it.startupStep.id } 56 | .forEach { 57 | printEvent(buffer, it, indent + 1, timeline) 58 | } 59 | } 60 | 61 | companion object { 62 | private val LOG = LoggerFactory.getLogger(ApplicationStartupPrinter::class.java) 63 | private const val TIME_COLUMN_WIDTH = 10 64 | private const val INDENT = 2 65 | private const val HEADER_WIDTH = 100 66 | } 67 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/main/java/com/latch/LoggingEventCloner.java: -------------------------------------------------------------------------------- 1 | package com.latch; 2 | 3 | import ch.qos.logback.classic.LoggerContext; 4 | import ch.qos.logback.classic.spi.ILoggingEvent; 5 | import ch.qos.logback.classic.spi.LoggingEvent; 6 | import org.slf4j.LoggerFactory; 7 | import org.slf4j.Marker; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /* 13 | * MIT License 14 | * 15 | * Copyright (c) 2019 Latchable, Inc. 16 | * 17 | * Permission is hereby granted, free of charge, to any person obtaining a copy 18 | * of this software and associated documentation files (the "Software"), to deal 19 | * in the Software without restriction, including without limitation the rights 20 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | * copies of the Software, and to permit persons to whom the Software is 22 | * furnished to do so, subject to the following conditions: 23 | * 24 | * The above copyright notice and this permission notice shall be included in all 25 | * copies or substantial portions of the Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | * SOFTWARE. 34 | */ 35 | class LoggingEventCloner { 36 | private static LoggerContext loggerContext; 37 | 38 | public static LoggingEvent clone(ILoggingEvent event, String message, Map mdcValueMap) { 39 | LoggingEvent newEvent = new LoggingEvent(); 40 | 41 | newEvent.setLevel(event.getLevel()); 42 | newEvent.setLoggerName(event.getLoggerName()); 43 | newEvent.setTimeStamp(event.getTimeStamp()); 44 | newEvent.setLoggerContextRemoteView(event.getLoggerContextVO()); 45 | newEvent.setLoggerContext(getLoggerContext()); 46 | newEvent.setThreadName(event.getThreadName()); 47 | newEvent.setMessage(message); 48 | newEvent.setMDCPropertyMap(mdcValueMap); 49 | 50 | List eventMarkers = event.getMarkerList(); 51 | if (eventMarkers != null && !eventMarkers.isEmpty()) { 52 | eventMarkers.forEach(newEvent::addMarker); 53 | } 54 | 55 | if (event.hasCallerData()) { 56 | newEvent.setCallerData(event.getCallerData()); 57 | } 58 | 59 | return newEvent; 60 | } 61 | 62 | /** 63 | * We can't set the logger context directly because that would cause issues if the logger context is not initialized yet. 64 | */ 65 | private static LoggerContext getLoggerContext() { 66 | if (loggerContext == null) { 67 | loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 68 | } 69 | 70 | return loggerContext; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-tracing/src/main/kotlin/io/cloudflight/platform/spring/tracing/filter/RequestLoggingFilter.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.tracing.filter 2 | 3 | import io.cloudflight.platform.spring.logging.mdc.mdcScope 4 | import jakarta.servlet.FilterChain 5 | import jakarta.servlet.http.HttpServletRequest 6 | import jakarta.servlet.http.HttpServletResponse 7 | import net.logstash.logback.argument.StructuredArguments.kv 8 | import org.slf4j.LoggerFactory 9 | import org.springframework.web.HttpRequestMethodNotSupportedException 10 | import org.springframework.web.filter.OncePerRequestFilter 11 | import org.springframework.web.servlet.HandlerMapping 12 | 13 | class RequestLoggingFilter(private val dataProvider: Collection) : OncePerRequestFilter() { 14 | 15 | override fun doFilterInternal( 16 | request: HttpServletRequest, 17 | response: HttpServletResponse, 18 | filterChain: FilterChain 19 | ) = mdcScope { 20 | val now = System.currentTimeMillis() 21 | val originalRequestUri: String = request.requestURI.toString() 22 | val requestIp = "${request.remoteAddr} / ${request.remoteHost}" 23 | 24 | MDC.put("requestUri", originalRequestUri) 25 | 26 | val originalQueryString: String? = request.queryString 27 | if (originalQueryString != null) { 28 | MDC.put("queryString", originalQueryString) 29 | } 30 | val requestPattern = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE) 31 | if (requestPattern != null) { 32 | MDC.put("requestPattern", requestPattern.toString()) 33 | } 34 | MDC.put("method", request.method) 35 | MDC.put("requestedFrom", requestIp) 36 | 37 | dataProvider.forEach { 38 | val data = it.provideData(request) 39 | data.forEach { d -> 40 | MDC.put(d.key, d.value) 41 | } 42 | } 43 | 44 | try { 45 | filterChain.doFilter(request, response) 46 | } catch (e: HttpRequestMethodNotSupportedException) { 47 | LOG.warn( 48 | "HttpRequestMethodNotSupportedException in CurrentUserLoggingFilter with url $originalRequestUri:", 49 | e 50 | ) 51 | } finally { 52 | if (LOG.isTraceEnabled) { 53 | with(request) { 54 | val requestHeaders = request.headerNames.toList().map { 55 | "$it:${request.getHeader(it)}" 56 | }.joinToString(";") 57 | 58 | val duration = System.currentTimeMillis() - now 59 | LOG.trace( 60 | "Request $method $originalRequestUri took $duration ms.", 61 | kv("status", response.status), 62 | kv("duration", duration), 63 | kv("requestHeaders", requestHeaders) 64 | ) 65 | } 66 | } 67 | } 68 | } 69 | 70 | companion object { 71 | private val LOG = LoggerFactory.getLogger("RequestLogger") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/main/kotlin/io/cloudflight/platform/spring/i18n/impl/PlatformMessageSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n.impl 2 | 3 | import io.cloudflight.platform.spring.i18n.ListResourceBundleMessageSource 4 | import org.slf4j.LoggerFactory 5 | import org.springframework.context.support.ReloadableResourceBundleMessageSource 6 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver 7 | import java.io.IOException 8 | import java.util.* 9 | 10 | 11 | internal class PlatformMessageSourceImpl : ReloadableResourceBundleMessageSource(), ListResourceBundleMessageSource { 12 | 13 | private val resourcePatternResolver = PathMatchingResourcePatternResolver() 14 | 15 | override fun getAllMessages(locale: Locale): Map { 16 | val propertiesHolder = getMergedProperties(locale) 17 | var result = propertiesToStringMap(propertiesHolder.properties) 18 | 19 | val parentListMessageSource = parentMessageSource as? ListResourceBundleMessageSource 20 | if (parentListMessageSource != null) { 21 | val parentMessages = parentListMessageSource.getAllMessages(locale) 22 | result = parentMessages.plus(result) 23 | } 24 | 25 | return result 26 | } 27 | 28 | override fun refreshProperties(filename: String, propHolder: PropertiesHolder?): PropertiesHolder { 29 | if (filename.startsWith(PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX)) { 30 | return refreshClassPathProperties(filename, propHolder); 31 | } else { 32 | return super.refreshProperties(filename, propHolder); 33 | } 34 | } 35 | 36 | private fun refreshClassPathProperties(filename: String, propHolder: PropertiesHolder?): PropertiesHolder { 37 | val properties = Properties() 38 | var lastModified: Long = -1 39 | try { 40 | val resources = resourcePatternResolver.getResources(filename + PROPERTIES_SUFFIX) 41 | for (resource in resources) { 42 | val sourcePath = resource.getURI() 43 | .toString() 44 | .replace(PROPERTIES_SUFFIX, "") 45 | val holder = super.refreshProperties(sourcePath, propHolder) 46 | properties.putAll(propertiesToStringMap(holder.properties)) 47 | if (lastModified < resource.lastModified()) { 48 | lastModified = resource.lastModified() 49 | } 50 | } 51 | } catch (e: IOException) { 52 | LOG.warn("Failed to load property file", e) 53 | } 54 | 55 | return PropertiesHolder(properties, lastModified) 56 | } 57 | 58 | private fun propertiesToStringMap(properties: Properties?): Map { 59 | return requireNotNull(properties) { "No properties defined" } 60 | .map { it.key.toString() to it.value.toString() } 61 | .toMap() 62 | } 63 | 64 | companion object { 65 | private val LOG = LoggerFactory.getLogger(PlatformMessageSourceImpl::class.java) 66 | private const val PROPERTIES_SUFFIX = ".properties" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/main/java/com/latch/SplittingAppenderBase.java: -------------------------------------------------------------------------------- 1 | package com.latch; 2 | 3 | import ch.qos.logback.core.Appender; 4 | import ch.qos.logback.core.UnsynchronizedAppenderBase; 5 | import ch.qos.logback.core.spi.AppenderAttachable; 6 | import ch.qos.logback.core.spi.AppenderAttachableImpl; 7 | 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | /* 12 | * MIT License 13 | * 14 | * Copyright (c) 2019 Latchable, Inc. 15 | * 16 | * Permission is hereby granted, free of charge, to any person obtaining a copy 17 | * of this software and associated documentation files (the "Software"), to deal 18 | * in the Software without restriction, including without limitation the rights 19 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | * copies of the Software, and to permit persons to whom the Software is 21 | * furnished to do so, subject to the following conditions: 22 | * 23 | * The above copyright notice and this permission notice shall be included in all 24 | * copies or substantial portions of the Software. 25 | * 26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | * SOFTWARE. 33 | */ 34 | public abstract class SplittingAppenderBase extends UnsynchronizedAppenderBase 35 | implements AppenderAttachable { 36 | 37 | private final AppenderAttachableImpl aai = new AppenderAttachableImpl<>(); 38 | 39 | protected abstract List split(E event); 40 | 41 | protected abstract boolean shouldSplit(E eventObject); 42 | 43 | @Override 44 | protected void append(E eventObject) { 45 | if (shouldSplit(eventObject)) { 46 | split(eventObject).forEach(aai::appendLoopOnAppenders); 47 | } else { 48 | aai.appendLoopOnAppenders(eventObject); 49 | } 50 | } 51 | 52 | public void addAppender(Appender newAppender) { 53 | addInfo("Attaching appender named [" + newAppender.getName() + "] to SplittingAppender."); 54 | aai.addAppender(newAppender); 55 | } 56 | 57 | public Iterator> iteratorForAppenders() { 58 | return aai.iteratorForAppenders(); 59 | } 60 | 61 | public Appender getAppender(String name) { 62 | return aai.getAppender(name); 63 | } 64 | 65 | public boolean isAttached(Appender eAppender) { 66 | return aai.isAttached(eAppender); 67 | } 68 | 69 | public void detachAndStopAllAppenders() { 70 | aai.detachAndStopAllAppenders(); 71 | } 72 | 73 | public boolean detachAppender(Appender eAppender) { 74 | return aai.detachAppender(eAppender); 75 | } 76 | 77 | public boolean detachAppender(String name) { 78 | return aai.detachAppender(name); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-validation/src/test/kotlin/io/cloudflight/platform/spring/validation/impl/ErrorResponseMapperTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.validation.impl 2 | 3 | import io.cloudflight.platform.spring.validation.api.dto.FieldMessageDto 4 | import io.cloudflight.platform.spring.validation.api.dto.GlobalMessageDto 5 | import io.cloudflight.platform.spring.validation.api.dto.MessageSeverity 6 | import io.mockk.every 7 | import io.mockk.mockk 8 | import org.assertj.core.api.Assertions.assertThat 9 | import org.junit.jupiter.api.BeforeEach 10 | import org.junit.jupiter.api.Test 11 | import org.springframework.context.MessageSource 12 | import org.springframework.context.i18n.LocaleContextHolder 13 | import org.springframework.validation.MapBindingResult 14 | import java.util.* 15 | 16 | internal class ErrorResponseMapperTest { 17 | 18 | val locale = Locale.ENGLISH 19 | 20 | val messageSource: MessageSource = mockk() 21 | 22 | val errorResponseMapper = 23 | ErrorResponseMapperImpl(messageSource) 24 | 25 | @BeforeEach 26 | fun setup() { 27 | LocaleContextHolder.setLocale(locale) 28 | } 29 | 30 | @Test 31 | fun `map binding result with field errors only`() { 32 | val fieldName = "myField" 33 | val errorCode = "core.my.error" 34 | val message = "My error message" 35 | val bindingResult = MapBindingResult(mapOf(), "testMap") 36 | bindingResult.rejectValue(fieldName, errorCode) 37 | 38 | every { 39 | messageSource.getMessage(match { it.codes!!.contains(errorCode) }, locale) 40 | } returns message 41 | 42 | val result = errorResponseMapper.mapBindingResult(bindingResult) 43 | 44 | val expectedFieldError = FieldMessageDto(fieldName, GlobalMessageDto(message, MessageSeverity.ERROR)) 45 | assertThat(result.globalMessages).isEmpty() 46 | assertThat(result.fieldMessages).hasSize(1) 47 | .first() 48 | .isEqualTo(expectedFieldError) 49 | } 50 | 51 | @Test 52 | fun `map binding result with global errors only`() { 53 | val errorCode = "core.my.error" 54 | val message = "My error message" 55 | val bindingResult = MapBindingResult(mapOf(), "testMap") 56 | bindingResult.reject(errorCode) 57 | 58 | every { 59 | messageSource.getMessage(match { it.codes!!.contains(errorCode) }, locale) 60 | } returns message 61 | 62 | val result = errorResponseMapper.mapBindingResult(bindingResult) 63 | 64 | val expectedGlobalError = GlobalMessageDto(message, MessageSeverity.ERROR) 65 | assertThat(result.globalMessages).hasSize(1) 66 | .first() 67 | .isEqualTo(expectedGlobalError) 68 | assertThat(result.fieldMessages).isEmpty() 69 | } 70 | 71 | @Test 72 | fun `map binding result without errors`() { 73 | val bindingResult = MapBindingResult(mapOf(), "testMap") 74 | 75 | val result = errorResponseMapper.mapBindingResult(bindingResult) 76 | 77 | assertThat(result.globalMessages).isEmpty() 78 | assertThat(result.fieldMessages).isEmpty() 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-messaging/src/test/kotlin/io/cloudflight/platform/spring/messaging/MessagingContainerTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.messaging 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.assertj.core.api.Fail.fail 5 | import org.awaitility.Awaitility.await 6 | import org.junit.jupiter.api.BeforeEach 7 | import org.junit.jupiter.api.Test 8 | import org.springframework.beans.factory.annotation.Autowired 9 | import org.springframework.boot.test.context.SpringBootTest 10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection 11 | import org.springframework.context.ApplicationContext 12 | import org.testcontainers.containers.RabbitMQContainer 13 | import org.testcontainers.junit.jupiter.Container 14 | import org.testcontainers.junit.jupiter.Testcontainers 15 | import java.time.Duration 16 | 17 | @SpringBootTest(classes = [MessagingTestApplication::class]) 18 | @Testcontainers 19 | class MessagingContainerTest( 20 | @Autowired private val applicationContext: ApplicationContext, 21 | @Autowired private val transactionalService: TransactionalService, 22 | @Autowired private val myListener: MyListener 23 | ) { 24 | 25 | companion object { 26 | @ServiceConnection 27 | @Container 28 | val rabbit: RabbitMQContainer = RabbitMQContainer() 29 | } 30 | 31 | @BeforeEach 32 | fun resetListener() { 33 | myListener.lastMessageReceived = null 34 | } 35 | 36 | @Test 37 | fun sendMessageAfterTransactionCommit() { 38 | transactionalService.performAction("foo", "bar") 39 | 40 | await().atMost(Duration.ofSeconds(2)).until { 41 | myListener.lastMessageReceived != null 42 | } 43 | 44 | with(myListener) { 45 | assertThat(lastMessageReceived).isNotNull 46 | assertThat(lastMessageReceived?.value1).isEqualTo("foo") 47 | assertThat(lastMessageReceivedTime!!).isGreaterThan(transactionalService.lastActionPerformed()!!) 48 | .`as`("listener must have been notified afterwards") 49 | assertThat(lastMessageReceivedThreadName).startsWith("myListener") 50 | } 51 | } 52 | 53 | @Test 54 | fun doNotSendMessageInCaseOfExceptionInsideTransaction() { 55 | try { 56 | transactionalService.performAction("foo", "throw") 57 | fail("method should throw an exception") 58 | } catch (ex: Exception) { 59 | // OK 60 | } 61 | 62 | Thread.sleep(2000) 63 | assertThat(myListener.lastMessageReceived).isNull() 64 | } 65 | 66 | @Test 67 | fun deactivateListener() { 68 | applicationContext.publishEvent(ProcessControlEvent("rabbit-myListener", false)) 69 | 70 | transactionalService.performAction("foo", "bar") 71 | 72 | Thread.sleep(2000) 73 | assertThat(myListener.lastMessageReceived).`as`("no message must have been received").isNull() 74 | applicationContext.publishEvent(ProcessControlEvent("rabbit-myListener", true)) 75 | 76 | await().atMost(Duration.ofSeconds(2)).until { 77 | myListener.lastMessageReceived != null 78 | } 79 | assertThat(myListener.lastMessageReceived).isNotNull 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-i18n/src/test/kotlin/io/cloudflight/platform/spring/i18n/I18nApplicationTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.i18n 2 | 3 | import io.cloudflight.platform.spring.i18n.autoconfigure.I18nProperties 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.Test 6 | import org.junit.jupiter.api.extension.ExtendWith 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc 9 | import org.springframework.boot.test.context.SpringBootTest 10 | import org.springframework.context.MessageSource 11 | import org.springframework.test.context.junit.jupiter.SpringExtension 12 | import org.springframework.test.web.servlet.MockMvc 13 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get 14 | import org.springframework.test.web.servlet.result.MockMvcResultHandlers 15 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath 16 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status 17 | import java.util.* 18 | 19 | @ExtendWith(SpringExtension::class) 20 | @SpringBootTest(classes = [I18nTestApplication::class]) 21 | @AutoConfigureMockMvc 22 | class I18nApplicationTest( 23 | @Autowired private val messageSource: MessageSource, 24 | @Autowired private val mockMvc: MockMvc, 25 | @Autowired private val i18nProperties: I18nProperties 26 | ) { 27 | 28 | @Test 29 | fun primaryLocaleInitialized() { 30 | assertThat(i18nProperties.primary!!.language) 31 | .isEqualToIgnoringCase("japanese") 32 | } 33 | 34 | @Test 35 | fun plainI18n() { 36 | assertThat(messageSource.getMessage("testMessage", null, Locale.JAPANESE)) 37 | .isEqualTo("testMessageValue-japanese") 38 | assertThat(messageSource.getMessage("testMessage", null, Locale.ENGLISH)) 39 | .isEqualTo("testMessageValue-english") 40 | } 41 | 42 | @Test 43 | fun i18nPlaceholders() { 44 | assertThat(messageSource.getMessage("testMessagePlaceholderFrontend", null, Locale.JAPANESE)) 45 | .isEqualTo("testMessageValueWithFrontendPlaceholder: {{somePlaceholder}}") 46 | 47 | assertThat(messageSource.getMessage("testMessagePlaceholderBackend", arrayOf("foo"), Locale.JAPANESE)) 48 | .isEqualTo("testMessageValueWithBackendPlaceholder: foo") 49 | } 50 | 51 | @Test 52 | fun httpEndpoint() { 53 | mockMvc.perform(get("/api/i18n/ja")) 54 | .andDo(MockMvcResultHandlers.print()) 55 | .andExpect(status().isOk()) 56 | .andExpect(jsonPath("$.testMessage").value("testMessageValue-japanese")) 57 | // both backend and frontend placeholders must go through unprocessed 58 | .andExpect(jsonPath("$.testMessagePlaceholderBackend").value("testMessageValueWithBackendPlaceholder: {0}")) 59 | .andExpect(jsonPath("$.testMessagePlaceholderFrontend").value("testMessageValueWithFrontendPlaceholder: {{somePlaceholder}}")); 60 | 61 | mockMvc.perform(get("/api/i18n/en")) 62 | .andDo(MockMvcResultHandlers.print()) 63 | .andExpect(status().isOk()) 64 | .andExpect(jsonPath("$.testMessage").value("testMessageValue-english")) 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | assertj = "3.27.6" 3 | 4 | azure = "5.23.0" 5 | azure-identity = "1.18.1" 6 | 7 | # we provide the latest version of guava here, as queryDSL pulls in a very old (and vulnerable) version of guava, and we want to override that here 8 | guava = "33.4.8-jre" 9 | 10 | jgiven = "1.3.1" 11 | jupiter = "5.14.1" 12 | 13 | kotlin-logging = "3.0.5" 14 | 15 | logstash = "7.4" 16 | 17 | opentelemetry = "1.56.0" 18 | 19 | shedlock = "6.10.0" 20 | 21 | spring-boot = "3.5.7" 22 | spring-cloud = "2025.0.0" 23 | 24 | [libraries] 25 | archunit-ccv = { module = "io.cloudflight.cleancode.archunit:archunit-cleancode-verifier", version = "0.5.0" } 26 | 27 | assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj" } 28 | 29 | awaitility = { module = "org.awaitility:awaitility", version = "4.3.0" } 30 | 31 | azure-dependencies-bom = { module = "com.azure.spring:spring-cloud-azure-dependencies", version.ref = "azure" } 32 | azure-identity = { module = "com.azure:azure-identity", version.ref = "azure-identity" } 33 | 34 | jackson-databind-nullable = { module = "org.openapitools:jackson-databind-nullable", version = "0.2.8" } 35 | 36 | jgiven-html5-report = { module = "com.tngtech.jgiven:jgiven-html5-report", version.ref = "jgiven" } 37 | jgiven-junit5 = { module = "com.tngtech.jgiven:jgiven-html5-report", version.ref = "jgiven" } 38 | jgiven-spring-junit5 = { module = "com.tngtech.jgiven:jgiven-spring-junit5", version.ref = "jgiven" } 39 | jgiven-kotlin = { module = "io.toolisticon.testing:jgiven-kotlin", version = "1.3.1.0" } 40 | 41 | junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "jupiter" } 42 | junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "jupiter" } 43 | junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "jupiter" } 44 | 45 | guava = { module = "com.google.guava:guava", version.ref = "guava" } 46 | kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version.ref = "kotlin-logging" } 47 | 48 | logback-logstash-encoder = { module = "net.logstash.logback:logstash-logback-encoder", version.ref = "logstash" } 49 | logback-classic = { module = "ch.qos.logback:logback-classic" } 50 | 51 | mockk = { module = "io.mockk:mockk-jvm", version = "1.13.11" } 52 | 53 | opentelemetry-bom = { module = "io.opentelemetry:opentelemetry-bom", version.ref = "opentelemetry" } 54 | 55 | quickperf-bom = { module = "org.quickperf:quick-perf-bom", version = "1.1.0" } 56 | 57 | shedlock-spring = { module = "net.javacrumbs.shedlock:shedlock-spring", version.ref = "shedlock" } 58 | shedlock-provider-redis-spring = { module = "net.javacrumbs.shedlock:shedlock-provider-redis-spring", version.ref = "shedlock" } 59 | 60 | spring-boot-bom = { module = "org.springframework.boot:spring-boot-dependencies", version.ref = "spring-boot" } 61 | spring-cloud-bom = { module = "org.springframework.cloud:spring-cloud-dependencies", version.ref = "spring-cloud" } 62 | 63 | springfox-swagger2 = { module = "io.springfox:springfox-swagger2", version = "3.0.0" } 64 | swagger-annotations2 = { module = "io.swagger:swagger-annotations", version = "1.6.16" } 65 | swagger-annotations3 = { module = "io.swagger.core.v3:swagger-annotations", version = "2.2.40" } 66 | 67 | testcontainers-junit4-mock = { module = "io.quarkus:quarkus-junit4-mock", version = "3.29.3" } 68 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-api/src/main/kotlin/io/cloudflight/platform/spring/storage/service/StorageService.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.service 2 | 3 | import io.cloudflight.platform.spring.storage.dto.* 4 | import org.springframework.http.MediaType 5 | import java.io.InputStream 6 | import java.time.Duration 7 | 8 | /** 9 | * The [StorageService] provides a common interface around cloud storages like Amazon S3 or Azure Storage. 10 | * It provides method for direct [StorageService.download] or [StorageService.upload] directly form the backend, 11 | * but also gives you the opportunity to create signed URLs towards your storage that you can pass to any client 12 | * to upload and download objects directly without going through your application server. 13 | * 14 | * As an abstraction we assume the underlying storage can handle containers (the equivalent in S3 are buckets) 15 | * and objects inside those containers, all of which have unique IDs. 16 | * 17 | * @author Klaus Lehner 18 | */ 19 | interface StorageService { 20 | 21 | /** 22 | * Generates a [StorageRequest] (typically a [StorageRequestType.GET]) towards the object at [storageLocation]. 23 | * The link will be valid for the given [validityDuration]. 24 | * 25 | * If the object does not exist, a [StorageObjectNotFoundException] is thrown. 26 | */ 27 | @Throws(StorageObjectNotFoundException::class) 28 | fun generateDownloadRequest(storageLocation: StorageLocation, validityDuration: Duration): StorageRequest 29 | 30 | /** 31 | * Downloads the storage object at [storageLocation] by returning an [InputStream]. 32 | * 33 | * It is the responsibility of the caller to close the stream. 34 | * 35 | * If the object does not exist, a [StorageObjectNotFoundException] is thrown. 36 | */ 37 | @Throws(StorageObjectNotFoundException::class) 38 | fun download(storageLocation: StorageLocation): InputStream 39 | 40 | fun generateUploadRequest( 41 | storageLocation: StorageLocation, 42 | validityDuration: Duration, 43 | contentType: MediaType? 44 | ): StorageRequest 45 | 46 | fun upload( 47 | storageLocation: StorageLocation, 48 | inputStream: InputStream, 49 | contentType: MediaType, 50 | timeout: Duration = Duration.ofMinutes(1) 51 | ) 52 | 53 | /** 54 | * Marks a storage object at the given [storageLocation] for deletion. 55 | * It's not guaranteed that the object is being physically deleted from the whole storage immediately. 56 | * 57 | * If the object does not exist, a [StorageObjectNotFoundException] is thrown. 58 | */ 59 | @Throws(StorageObjectNotFoundException::class) 60 | fun markForDeletion(storageLocation: StorageLocation) 61 | 62 | /** 63 | * lists the name of all storage objects inside a given [container]. 64 | * If the container does not exist, no container is being created and 65 | * an empty list is returned 66 | */ 67 | fun listObjectNames(container: String, prefix: String? = null): Set 68 | 69 | /** 70 | * Checks if a an object at given [storageLocation] exists. 71 | */ 72 | fun existsObject(storageLocation: StorageLocation): Boolean 73 | 74 | /** 75 | * Gets the properties of a blob at a given [storageLocation] 76 | */ 77 | fun getObjectProperties(storageLocation: StorageLocation): ObjectProperties 78 | } 79 | 80 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/main/java/com/latch/LengthSplittingAppender.java: -------------------------------------------------------------------------------- 1 | package com.latch; 2 | 3 | import ch.qos.logback.classic.LoggerContext; 4 | import ch.qos.logback.classic.spi.ILoggingEvent; 5 | import ch.qos.logback.classic.spi.LoggingEvent; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /* 14 | * MIT License 15 | * 16 | * Copyright (c) 2019 Latchable, Inc. 17 | * 18 | * Permission is hereby granted, free of charge, to any person obtaining a copy 19 | * of this software and associated documentation files (the "Software"), to deal 20 | * in the Software without restriction, including without limitation the rights 21 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | * copies of the Software, and to permit persons to whom the Software is 23 | * furnished to do so, subject to the following conditions: 24 | * 25 | * The above copyright notice and this permission notice shall be included in all 26 | * copies or substantial portions of the Software. 27 | * 28 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | * SOFTWARE. 35 | */ 36 | public class LengthSplittingAppender extends SplittingAppenderBase { 37 | 38 | private int maxLength; 39 | private String sequenceKey; 40 | 41 | public int getMaxLength() { 42 | return maxLength; 43 | } 44 | 45 | public void setMaxLength(int maxLength) { 46 | this.maxLength = maxLength; 47 | } 48 | 49 | public String getSequenceKey() { 50 | return sequenceKey; 51 | } 52 | 53 | public void setSequenceKey(String sequenceKey) { 54 | this.sequenceKey = sequenceKey; 55 | } 56 | 57 | @Override 58 | public boolean shouldSplit(ILoggingEvent event) { 59 | return event.getFormattedMessage().length() > maxLength; 60 | } 61 | 62 | @Override 63 | public List split(ILoggingEvent event) { 64 | List logMessages = splitString(event.getFormattedMessage(), getMaxLength()); 65 | 66 | List splitLogEvents = new ArrayList<>(logMessages.size()); 67 | for (int i = 0; i < logMessages.size(); i++) { 68 | String message = logMessages.get(i); 69 | Map seqMDCPropertyMap = new HashMap<>(event.getMDCPropertyMap()); 70 | seqMDCPropertyMap.put(getSequenceKey(), Integer.toString(i)); 71 | 72 | LoggingEvent clonedEvent = LoggingEventCloner.clone(event, message, seqMDCPropertyMap); 73 | 74 | splitLogEvents.add(clonedEvent); 75 | } 76 | 77 | return splitLogEvents; 78 | } 79 | 80 | private List splitString(String str, int chunkSize) { 81 | int fullChunks = str.length() / chunkSize; 82 | int remainder = str.length() % chunkSize; 83 | 84 | List results = new ArrayList<>(remainder == 0 ? fullChunks : fullChunks + 1); 85 | for (int i = 0; i < fullChunks; i++) { 86 | results.add(str.substring(i * chunkSize, i * chunkSize + chunkSize)); 87 | } 88 | if (remainder != 0) { 89 | results.add(str.substring(str.length() - remainder)); 90 | } 91 | return results; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-caching/src/test/kotlin/io/cloudflight/platform/spring/caching/EvictCacheErrorHandlerTest.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.caching 2 | 3 | import org.junit.jupiter.api.AfterEach 4 | import org.junit.jupiter.api.BeforeEach 5 | import org.junit.jupiter.api.Test 6 | import org.junit.jupiter.api.assertThrows 7 | import org.mockito.BDDMockito.given 8 | import org.mockito.BDDMockito.willThrow 9 | import org.mockito.Mockito.mock 10 | import org.mockito.Mockito.verify 11 | import org.springframework.cache.Cache 12 | import org.springframework.cache.CacheManager 13 | import org.springframework.cache.annotation.CacheConfig 14 | import org.springframework.cache.annotation.Cacheable 15 | import org.springframework.cache.annotation.CachingConfigurer 16 | import org.springframework.cache.annotation.EnableCaching 17 | import org.springframework.cache.interceptor.CacheErrorHandler 18 | import org.springframework.cache.support.SimpleCacheManager 19 | import org.springframework.context.annotation.AnnotationConfigApplicationContext 20 | import org.springframework.context.annotation.Bean 21 | import org.springframework.context.annotation.Configuration 22 | import org.springframework.data.redis.serializer.SerializationException 23 | import java.util.concurrent.atomic.AtomicLong 24 | 25 | 26 | class EvictCacheErrorHandlerTest { 27 | 28 | private var context: AnnotationConfigApplicationContext? = null 29 | 30 | private var cache: Cache? = null 31 | 32 | private var errorHandler: CacheErrorHandler? = null 33 | 34 | private var simpleService: SimpleService? = null 35 | 36 | 37 | @BeforeEach 38 | fun setup() { 39 | this.context = AnnotationConfigApplicationContext(Config::class.java) 40 | this.cache = context!!.getBean("mockCache", Cache::class.java) 41 | this.errorHandler = context!!.getBean(CacheErrorHandler::class.java) 42 | this.simpleService = context!!.getBean(SimpleService::class.java) 43 | } 44 | 45 | 46 | @AfterEach 47 | fun closeContext() { 48 | context!!.close() 49 | } 50 | 51 | @Test 52 | fun getFail() { 53 | val exception = SerializationException("Deserialization exception") 54 | willThrow(exception).given(this.cache)!!.get(0L) 55 | 56 | simpleService!!.get(0L) 57 | verify(this.cache!!).get(0L) 58 | verify(this.cache!!).evict(0L) // result of the invocation 59 | } 60 | 61 | @Test 62 | fun getFailException() { 63 | val exception = IllegalStateException("Wrong type exception") 64 | willThrow(exception).given(this.cache)!!.get(0L) 65 | 66 | assertThrows { simpleService!!.get(0L) } 67 | verify(this.cache)!!.get(0L) 68 | } 69 | 70 | @Configuration 71 | @EnableCaching 72 | class Config : CachingConfigurer { 73 | @Bean 74 | override fun errorHandler(): CacheErrorHandler { 75 | return EvictCacheErrorHandler() 76 | } 77 | 78 | @Bean 79 | fun simpleService(): SimpleService { 80 | return SimpleService() 81 | } 82 | 83 | @Bean 84 | override fun cacheManager(): CacheManager { 85 | val cacheManager = SimpleCacheManager() 86 | cacheManager.setCaches(listOf(mockCache())) 87 | return cacheManager 88 | } 89 | 90 | @Bean 91 | fun mockCache(): Cache { 92 | val cache: Cache = mock() 93 | given(cache.name).willReturn("test") 94 | return cache 95 | } 96 | } 97 | 98 | @CacheConfig(cacheNames = ["test"]) 99 | open class SimpleService { 100 | private val counter = AtomicLong() 101 | 102 | @Cacheable 103 | open fun get(id: Long): Any { 104 | return counter.getAndIncrement() 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-monitoring/src/main/kotlin/io/cloudflight/platform/spring/monitoring/autoconfigure/ManagementSecurityAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.monitoring.autoconfigure 2 | 3 | import io.cloudflight.platform.spring.context.ApplicationContextProfiles 4 | import org.springframework.boot.SpringApplication 5 | import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest 6 | import org.springframework.boot.autoconfigure.AutoConfiguration 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass 8 | import org.springframework.boot.env.EnvironmentPostProcessor 9 | import org.springframework.context.annotation.Bean 10 | import org.springframework.core.Ordered 11 | import org.springframework.core.annotation.Order 12 | import org.springframework.core.env.ConfigurableEnvironment 13 | import org.springframework.core.env.MapPropertySource 14 | import org.springframework.core.env.Profiles 15 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 16 | import org.springframework.security.config.annotation.web.builders.WebSecurity 17 | import org.springframework.security.config.annotation.web.invoke 18 | import org.springframework.security.web.SecurityFilterChain 19 | 20 | @AutoConfiguration 21 | @ConditionalOnClass(WebSecurity::class) 22 | class ManagementSecurityAutoConfiguration { 23 | 24 | /** 25 | * Deactivates Spring Security for all management endpoints. The order of this [SecurityFilterChain] 26 | * is set to [ORDER], and that should come before your individual [SecurityFilterChain] implementations, 27 | * so your order should be higher. 28 | */ 29 | @Bean 30 | @Order(ORDER) 31 | fun managementEndpointFilter(http: HttpSecurity): SecurityFilterChain { 32 | http { 33 | securityMatcher(EndpointRequest.toAnyEndpoint()) 34 | 35 | csrf { 36 | disable() 37 | } 38 | 39 | cors { 40 | disable() 41 | } 42 | 43 | authorizeHttpRequests { 44 | authorize(EndpointRequest.toAnyEndpoint(), permitAll) 45 | } 46 | } 47 | return http.build() 48 | } 49 | 50 | companion object { 51 | const val ORDER = 50 52 | } 53 | } 54 | 55 | /** 56 | * Sets the port of the management endpoints to [server.port] - 1000 57 | */ 58 | @Order(Ordered.LOWEST_PRECEDENCE) 59 | class ManagementServerPortEnvironmentPostProcessor : EnvironmentPostProcessor { 60 | 61 | override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) { 62 | if (!environment.acceptsProfiles( 63 | Profiles.of( 64 | ApplicationContextProfiles.TEST, 65 | ApplicationContextProfiles.TEST_CONTAINER 66 | ) 67 | ) 68 | ) { 69 | // we do not change the port for the management services in test cases and container tests to not get any conflicts 70 | // with concurrent builds 71 | (environment.getProperty(SERVER_PORT_NAME) ?: SPRING_DEFAULT_PORT).let { serverPort -> 72 | if (environment.getProperty(MANAGEMENT_SERVER_PORT_NAME).isNullOrBlank()) { 73 | // only change unless already set 74 | environment.propertySources.addFirst( 75 | MapPropertySource( 76 | "cloudflight-management", 77 | mapOf(MANAGEMENT_SERVER_PORT_NAME to serverPort.toInt() + 10000) 78 | ) 79 | ) 80 | } 81 | } 82 | } 83 | } 84 | 85 | companion object { 86 | const val SPRING_DEFAULT_PORT = "8080" 87 | 88 | const val SERVER_PORT_NAME = "server.port" 89 | const val MANAGEMENT_SERVER_PORT_NAME = "management.server.port" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/test/kotlin/io/cloudflight/platform/spring/storage/azure/Controller.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.azure 2 | 3 | import com.azure.storage.blob.BlobContainerClient 4 | import com.azure.storage.blob.BlobServiceClient 5 | import com.azure.storage.blob.BlobServiceClientBuilder 6 | import io.cloudflight.platform.spring.storage.dto.ObjectProperties 7 | import io.cloudflight.platform.spring.storage.dto.StorageLocation 8 | import io.cloudflight.platform.spring.storage.dto.StorageRequest 9 | import io.cloudflight.platform.spring.storage.service.StorageService 10 | import org.springframework.http.MediaType 11 | import org.springframework.http.ResponseEntity 12 | import org.springframework.web.bind.annotation.* 13 | import java.io.ByteArrayInputStream 14 | import java.time.Duration 15 | 16 | @RestController 17 | class Controller(blobServiceClientBuilder: BlobServiceClientBuilder, private val storageService: StorageService) { 18 | 19 | private val client: BlobServiceClient = blobServiceClientBuilder.buildClient() 20 | 21 | @PutMapping("containers/{containerName}/{blobName}", consumes = ["text/plain"]) 22 | fun postContent( 23 | @PathVariable containerName: String, 24 | @PathVariable blobName: String, 25 | @RequestBody content: String 26 | ): ResponseEntity { 27 | storageService.upload( 28 | StorageLocation(containerName, blobName), 29 | ByteArrayInputStream(content.toByteArray()), 30 | MediaType.TEXT_PLAIN 31 | ) 32 | return ResponseEntity.ok("") 33 | } 34 | 35 | @GetMapping("containers/{containerName}/{blobName}", produces = ["text/plain"]) 36 | fun getContent(@PathVariable containerName: String, @PathVariable blobName: String) = 37 | createOrGetContainer(containerName).getText(blobName) 38 | 39 | @GetMapping("object-count/{containerName}", produces = ["text/plain"]) 40 | fun getContent(@PathVariable containerName: String): Int = storageService.listObjectNames(containerName).size 41 | 42 | @GetMapping("download-url/{containerName}/{blobName}", produces = ["text/plain"]) 43 | fun getDownloadUrl(@PathVariable containerName: String, @PathVariable blobName: String): String { 44 | return storageService.generateDownloadRequest( 45 | StorageLocation(containerName, blobName), 46 | Duration.ofMinutes(10) 47 | ).url 48 | } 49 | 50 | @GetMapping("upload-url/{containerName}/{blobName}") 51 | fun getUploadUrl( 52 | @PathVariable containerName: String, 53 | @PathVariable blobName: String, 54 | @RequestParam contentType: MediaType 55 | ): StorageRequest { 56 | return storageService.generateUploadRequest( 57 | StorageLocation(containerName, blobName), 58 | Duration.ofMinutes(10), 59 | contentType 60 | ) 61 | } 62 | 63 | @DeleteMapping("delete/{containerName}/{blobName}") 64 | fun markForDeletion( 65 | @PathVariable containerName: String, 66 | @PathVariable blobName: String, 67 | ) { 68 | storageService.markForDeletion( 69 | StorageLocation(containerName, blobName) 70 | ) 71 | } 72 | 73 | @GetMapping("metadata/{containerName}/{blobName}") 74 | fun getBlobMetadata( 75 | @PathVariable containerName: String, 76 | @PathVariable blobName: String 77 | ): ObjectProperties { 78 | return storageService.getObjectProperties( 79 | StorageLocation(containerName, blobName) 80 | ) 81 | } 82 | 83 | fun createOrGetContainer(containerName: String): BlobContainerClient { 84 | // TODO caching 85 | val containerClient = client.getBlobContainerClient(containerName) 86 | if (!containerClient.exists()) { 87 | containerClient.create() 88 | } 89 | return containerClient 90 | } 91 | 92 | fun BlobContainerClient.getText(blobName: String) = 93 | getBlobClient(blobName).openInputStream().use { String(it.readAllBytes()) } 94 | } 95 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging-server-config/src/test/java/com/latch/LoggingEventClonerTest.java: -------------------------------------------------------------------------------- 1 | package com.latch; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import ch.qos.logback.classic.LoggerContext; 5 | import ch.qos.logback.classic.spi.LoggingEvent; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | import org.slf4j.LoggerFactory; 9 | import org.slf4j.Marker; 10 | import org.slf4j.helpers.BasicMarkerFactory; 11 | 12 | import java.util.Collections; 13 | import java.util.Map; 14 | 15 | public class LoggingEventClonerTest { 16 | @Test 17 | public void correctlyClonesBasicEventProperties() { 18 | LoggingEvent event = createLoggingEventWithContext(); 19 | event.setLevel(Level.DEBUG); 20 | event.setLoggerName("loggerName"); 21 | event.setThreadName("testThread"); 22 | event.setTimeStamp(System.currentTimeMillis()); 23 | 24 | LoggingEvent clonedEvent = LoggingEventCloner.clone(event, "", Collections.emptyMap()); 25 | 26 | Assertions.assertNotNull(clonedEvent); 27 | Assertions.assertEquals(event.getLevel(), clonedEvent.getLevel()); 28 | Assertions.assertEquals(event.getLoggerName(), clonedEvent.getLoggerName()); 29 | Assertions.assertEquals(event.getThreadName(), clonedEvent.getThreadName()); 30 | Assertions.assertEquals(event.getTimeStamp(), clonedEvent.getTimeStamp()); 31 | } 32 | 33 | @Test 34 | public void correctlyClonesMessage() { 35 | LoggingEvent event = createLoggingEventWithContext(); 36 | String message = "Test message"; 37 | 38 | LoggingEvent clonedEvent = LoggingEventCloner.clone(event, message, Collections.emptyMap()); 39 | 40 | Assertions.assertNotNull(clonedEvent); 41 | Assertions.assertEquals(message, clonedEvent.getMessage()); 42 | } 43 | 44 | @Test 45 | public void correctlyClonesMDCProperties() { 46 | LoggingEvent event = createLoggingEventWithContext(); 47 | Map mdcProperties = Map.of("key1", "value1", "key2", "value2"); 48 | 49 | LoggingEvent clonedEvent = LoggingEventCloner.clone(event, "", mdcProperties); 50 | 51 | Assertions.assertNotNull(clonedEvent); 52 | Map clonedMDCProperties = clonedEvent.getMDCPropertyMap(); 53 | Assertions.assertEquals(2, clonedMDCProperties.size()); 54 | Assertions.assertEquals("value1", clonedMDCProperties.get("key1")); 55 | Assertions.assertEquals("value2", clonedMDCProperties.get("key2")); 56 | } 57 | 58 | @Test 59 | public void correctlyClonesMarker() { 60 | LoggingEvent event = createLoggingEventWithContext(); 61 | Marker marker = new BasicMarkerFactory().getMarker("TestMarker"); 62 | event.addMarker(marker); 63 | 64 | LoggingEvent clonedEvent = LoggingEventCloner.clone(event, "", Collections.emptyMap()); 65 | 66 | Assertions.assertNotNull(clonedEvent); 67 | Assertions.assertEquals(marker.getName(), clonedEvent.getMarkerList().get(0).getName()); 68 | } 69 | 70 | @Test 71 | public void correctlyClonesCallerData() { 72 | LoggingEvent event = createLoggingEventWithContext(); 73 | StackTraceElement[] callerData = new StackTraceElement[] { 74 | new StackTraceElement("com.example.Class", "method", "Class.java", 42) 75 | }; 76 | event.setCallerData(callerData); 77 | 78 | LoggingEvent clonedEvent = LoggingEventCloner.clone(event, "", Collections.emptyMap()); 79 | 80 | Assertions.assertTrue(clonedEvent.hasCallerData()); 81 | StackTraceElement[] clonedCallerData = clonedEvent.getCallerData(); 82 | Assertions.assertEquals(1, clonedCallerData.length); 83 | Assertions.assertEquals(callerData[0].getClassLoaderName(), clonedCallerData[0].getClassLoaderName()); 84 | } 85 | 86 | private LoggingEvent createLoggingEventWithContext() { 87 | LoggingEvent event = new LoggingEvent(); 88 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 89 | event.setLoggerContext(loggerContext); 90 | return event; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-logging/src/main/java/io/cloudflight/platform/spring/logging/interceptor/LogParamInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.logging.interceptor; 2 | 3 | import io.cloudflight.platform.spring.logging.annotation.LogParam; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.aspectj.lang.ProceedingJoinPoint; 6 | import org.aspectj.lang.annotation.Around; 7 | import org.aspectj.lang.annotation.Aspect; 8 | import org.aspectj.lang.reflect.MethodSignature; 9 | import org.slf4j.MDC; 10 | import org.springframework.core.DefaultParameterNameDiscoverer; 11 | import org.springframework.core.ParameterNameDiscoverer; 12 | import org.springframework.core.annotation.AnnotatedElementUtils; 13 | import org.springframework.expression.Expression; 14 | import org.springframework.expression.ExpressionParser; 15 | import org.springframework.expression.spel.standard.SpelExpressionParser; 16 | 17 | import java.lang.reflect.Method; 18 | import java.lang.reflect.Parameter; 19 | import java.util.*; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | 22 | /** 23 | * Aspect for interception a method call which has one or more parameters annotated 24 | * with one or more {@link LogParam} annotations. 25 | * 26 | * @author Clemens Grabmann 27 | */ 28 | @Aspect 29 | public class LogParamInterceptor { 30 | 31 | private final ExpressionParser expressionParser = new SpelExpressionParser(); 32 | private final Map expressionMap = new ConcurrentHashMap<>(); 33 | private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); 34 | 35 | @Around("execution(public * *..*(.., @io.cloudflight.platform.spring.logging.annotation.LogParams (*), ..)) || " 36 | + "execution(public * *..*(.., @io.cloudflight.platform.spring.logging.annotation.LogParam (*), ..))") 37 | public Object addLogParam(final ProceedingJoinPoint joinPoint) throws Throwable { 38 | Set mdcKeys = new HashSet<>(); 39 | if (joinPoint.getSignature() instanceof MethodSignature methodSignature) { 40 | Method method = methodSignature.getMethod(); 41 | Parameter[] params = method.getParameters(); 42 | Object[] args = joinPoint.getArgs(); 43 | String[] parameterNames = parameterNameDiscoverer.getParameterNames(method); 44 | 45 | for (int i = 0; i < params.length; ++i) { 46 | Parameter param = params[i]; 47 | Object arg = args[i]; 48 | String paramName = parameterNames != null ? parameterNames[i] : param.getName(); 49 | mdcKeys.addAll(processParameter(param, paramName, arg)); 50 | } 51 | } 52 | 53 | try { 54 | return joinPoint.proceed(); 55 | } finally { 56 | for (String key : mdcKeys) { 57 | MDC.remove(key); 58 | } 59 | } 60 | } 61 | 62 | private Collection processParameter(final Parameter param, String parameterName, final Object arg) { 63 | Collection logParams = AnnotatedElementUtils.getMergedRepeatableAnnotations(param, LogParam.class); 64 | Collection mdcKeys = new ArrayList<>(logParams.size()); 65 | for (LogParam logParam : logParams) { 66 | String name; 67 | if (StringUtils.isNotBlank(logParam.name())) { 68 | name = logParam.name(); 69 | } else { 70 | name = parameterName; 71 | } 72 | 73 | Object value = arg; 74 | if (StringUtils.isNotEmpty(logParam.field())) { 75 | final Expression expression = expressionMap.computeIfAbsent( 76 | logParam, 77 | k -> expressionParser.parseExpression(logParam.field()) 78 | ); 79 | value = expression.getValue(arg); 80 | if (StringUtils.isBlank(logParam.name())) { 81 | name += ("." + logParam.field()); 82 | } 83 | } 84 | 85 | MDC.put(name, Objects.toString(value)); 86 | mdcKeys.add(name); 87 | } 88 | 89 | return mdcKeys; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-cloud-storage/platform-spring-cloud-storage-azure/src/main/kotlin/io/cloudflight/platform/spring/storage/azure/autoconfigure/PlatformAzureStorageBlobAutoConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.storage.azure.autoconfigure 2 | 3 | import com.azure.core.credential.AzureNamedKeyCredential 4 | import com.azure.core.credential.TokenCredential 5 | import com.azure.identity.DefaultAzureCredentialBuilder 6 | import com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.STORAGE_BLOB_CLIENT_BUILDER_BEAN_NAME 7 | import com.azure.spring.cloud.autoconfigure.implementation.storage.blob.AzureStorageBlobAutoConfiguration 8 | import com.azure.spring.cloud.autoconfigure.implementation.storage.blob.properties.AzureStorageBlobProperties 9 | import com.azure.storage.blob.BlobServiceClient 10 | import com.azure.storage.blob.BlobServiceClientBuilder 11 | import io.cloudflight.platform.spring.autoconfigure.azurite.AzuriteConnectionDetails 12 | import io.cloudflight.platform.spring.context.ApplicationContextProfiles 13 | import io.cloudflight.platform.spring.storage.azure.service.AzureSasTokenStrategy 14 | import io.cloudflight.platform.spring.storage.azure.service.AzureStorageService 15 | import io.cloudflight.platform.spring.storage.azure.service.AzuriteSasTokenStrategy 16 | import io.cloudflight.platform.spring.storage.azure.service.SasTokenStrategy 17 | import io.cloudflight.platform.spring.storage.service.StorageService 18 | import org.springframework.boot.autoconfigure.AutoConfiguration 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty 22 | import org.springframework.context.annotation.Bean 23 | import org.springframework.context.annotation.Profile 24 | 25 | /** 26 | * Provides a different [BlobServiceClientBuilder] than the one defined in [AzureStorageBlobAutoConfiguration] 27 | * when the staging or production profile is active in order to connect to the azure storage using the 28 | * [DefaultAzureCredentialBuilder] instead of connecting to the local azurite storage. 29 | */ 30 | @AutoConfiguration(before = [AzureStorageBlobAutoConfiguration::class]) 31 | @ConditionalOnProperty(value = ["spring.cloud.azure.storage.blob.enabled"], havingValue = "true", matchIfMissing = true) 32 | class PlatformAzureStorageBlobAutoConfiguration { 33 | 34 | @Bean(name = [STORAGE_BLOB_CLIENT_BUILDER_BEAN_NAME]) 35 | @Profile(ApplicationContextProfiles.PRODUCTION, ApplicationContextProfiles.STAGING) 36 | fun blobServiceClientBuilder( 37 | storageProperties: AzureStorageBlobProperties, 38 | tokenCredential: TokenCredential 39 | ): BlobServiceClientBuilder { 40 | return BlobServiceClientBuilder() 41 | .endpoint(storageProperties.endpoint) 42 | .credential(tokenCredential) 43 | } 44 | 45 | @Bean(name = [STORAGE_BLOB_CLIENT_BUILDER_BEAN_NAME]) 46 | @ConditionalOnBean(value = [AzuriteConnectionDetails::class]) 47 | fun azuriteBlobServiceClientBuilder(connectionDetails: AzuriteConnectionDetails): BlobServiceClientBuilder { 48 | return BlobServiceClientBuilder() 49 | .endpoint(connectionDetails.accountEndpoint) 50 | .credential(AzureNamedKeyCredential(connectionDetails.accountName, connectionDetails.accountKey)) 51 | } 52 | 53 | @Bean 54 | @ConditionalOnMissingBean 55 | fun tokenCredential(): TokenCredential { 56 | return DefaultAzureCredentialBuilder().build() 57 | } 58 | 59 | @Bean 60 | fun blobServiceClient(blobServiceClientBuilder: BlobServiceClientBuilder): BlobServiceClient { 61 | return blobServiceClientBuilder.buildClient() 62 | } 63 | 64 | @Bean 65 | fun azureStorageService(client: BlobServiceClient, sasTokenStrategy: SasTokenStrategy): StorageService { 66 | return AzureStorageService(client, sasTokenStrategy) 67 | } 68 | 69 | @Bean 70 | @Profile(ApplicationContextProfiles.DEVELOPMENT, ApplicationContextProfiles.TEST) 71 | fun azuriteTokenStrategy(): SasTokenStrategy { 72 | return AzuriteSasTokenStrategy() 73 | } 74 | 75 | @Bean 76 | @Profile(ApplicationContextProfiles.PRODUCTION, ApplicationContextProfiles.STAGING) 77 | fun azureTokenStrategy(client: BlobServiceClient): SasTokenStrategy { 78 | return AzureSasTokenStrategy(client) 79 | } 80 | } -------------------------------------------------------------------------------- /platform-spring-bom/platform-spring-server-config/src/main/kotlin/io/cloudflight/platform/spring/server/PlatformServerModuleStartupListener.kt: -------------------------------------------------------------------------------- 1 | package io.cloudflight.platform.spring.server 2 | 3 | import io.cloudflight.platform.spring.context.ApplicationContextProfiles 4 | import org.slf4j.LoggerFactory 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.boot.context.event.ApplicationStartedEvent 7 | import org.springframework.boot.info.BuildProperties 8 | import org.springframework.boot.info.GitProperties 9 | import org.springframework.context.ApplicationListener 10 | import org.springframework.core.env.Environment 11 | import org.springframework.core.env.Profiles 12 | import org.springframework.core.io.ClassPathResource 13 | import org.springframework.core.io.support.PropertiesLoaderUtils 14 | import org.springframework.stereotype.Service 15 | import java.time.Instant 16 | import java.util.* 17 | 18 | @Service 19 | class PlatformServerModuleStartupListener( 20 | @Autowired(required = false) private val gitProperties: GitProperties?, 21 | @Autowired(required = false) private val buildProperties: BuildProperties?, 22 | environment: Environment 23 | ) : ServerModuleIdentification, ApplicationListener { 24 | 25 | private val group: String 26 | private val id: String 27 | private val idShort: String? 28 | private val time: Instant? 29 | private val name: String 30 | private val version: String 31 | 32 | init { 33 | if (gitProperties != null && buildProperties != null) { 34 | group = buildProperties.group 35 | id = gitProperties.commitId 36 | idShort = gitProperties.shortCommitId 37 | time = gitProperties.commitTime 38 | name = buildProperties.name 39 | version = buildProperties.version 40 | } else { 41 | val devProperties = ClassPathResource("development.properties") 42 | if (devProperties.exists()) { 43 | val properties = PropertiesLoaderUtils.loadProperties(devProperties) 44 | group = properties.getProperty("development.group") 45 | name = properties.getProperty("development.name") 46 | version = properties.getProperty("development.version") 47 | id = UUID.randomUUID().toString() 48 | idShort = null 49 | time = null 50 | 51 | val acceptableDevProfiles = Profiles.of( 52 | ApplicationContextProfiles.DEVELOPMENT, 53 | ApplicationContextProfiles.TEST, 54 | ApplicationContextProfiles.TEST_CONTAINER 55 | ) 56 | if (!environment.acceptsProfiles(acceptableDevProfiles)) { 57 | LOG.warn("development.properties exists but none of the profiles $acceptableDevProfiles is applied.") 58 | } 59 | 60 | } else { 61 | LOG.warn("Neither GitProperties & BuildProperties nor development.properties are available. " + 62 | "Are you using the Cloudflight Gradle Plugin in a -server module?") 63 | group = buildProperties?.group ?: "unknown" 64 | id = gitProperties?.commitId ?: "unknown" 65 | idShort = gitProperties?.shortCommitId 66 | time = gitProperties?.commitTime 67 | name = buildProperties?.name ?: "unknown" 68 | version = buildProperties?.version ?: "unknown" 69 | } 70 | } 71 | } 72 | 73 | override fun onApplicationEvent(event: ApplicationStartedEvent) { 74 | LOG.info("Module $group:$name started up with version $version and id $id") 75 | } 76 | 77 | companion object { 78 | private val LOG = LoggerFactory.getLogger(PlatformServerModuleStartupListener::class.java) 79 | } 80 | 81 | override fun getGroup(): String { 82 | return this.group 83 | } 84 | 85 | override fun getName(): String { 86 | return this.name 87 | } 88 | 89 | override fun getId(): String { 90 | return this.id 91 | } 92 | 93 | override fun getIdShort(): String? = 94 | this.idShort 95 | 96 | override fun getTime(): Instant? = 97 | this.time 98 | 99 | override fun getVersion(): String { 100 | return this.version 101 | } 102 | } 103 | 104 | --------------------------------------------------------------------------------