Moved generic value classes to separate module

This commit is contained in:
2023-10-26 19:06:26 +02:00
parent 50ec898257
commit b3a55af93a
55 changed files with 264 additions and 257 deletions
+5
View File
@@ -0,0 +1,5 @@
plugins {
id("twomartens.kotlin")
id("twomartens.java")
kotlin("kapt")
}
@@ -1,4 +1,4 @@
package de.twomartens.timetable.model.base
package de.twomartens.timetable.types
@JvmInline
value class Hour private constructor(val value: Int) {
@@ -1,4 +1,4 @@
package de.twomartens.timetable.model.base
package de.twomartens.timetable.types
import java.time.LocalDate
import java.time.LocalDateTime
@@ -1,4 +1,4 @@
package de.twomartens.timetable.model.base
package de.twomartens.timetable.types
@JvmInline
value class NonEmptyString(val name: String) {
@@ -1,4 +1,4 @@
package de.twomartens.timetable.model.base
package de.twomartens.timetable.types
@JvmInline
value class PositiveInteger(val value: Int) {
+1
View File
@@ -5,6 +5,7 @@ plugins {
}
dependencies {
implementation(project(":common"))
implementation(libs.mapstruct.base)
implementation(libs.bundles.spring.boot.security)
implementation(libs.jaxb.impl)
@@ -1,7 +1,7 @@
package de.twomartens.timetable.bahnApi.mapper
import de.twomartens.timetable.bahnApi.model.db.BahnTimetable
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import org.mapstruct.*
import java.time.LocalDateTime
@@ -1,7 +1,7 @@
package de.twomartens.timetable.bahnApi.model
import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.types.HourAtDay
import org.springframework.stereotype.Component
import java.time.LocalDateTime
@@ -1,7 +1,7 @@
package de.twomartens.timetable.bahnApi.model.db
import de.twomartens.timetable.bahnApi.model.Eva
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import org.bson.types.ObjectId
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id
@@ -2,7 +2,7 @@ package de.twomartens.timetable.bahnApi.model.db
import de.twomartens.timetable.bahnApi.model.Eva
import de.twomartens.timetable.bahnApi.model.dto.BahnStationStop
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import org.bson.types.ObjectId
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id
@@ -1,7 +1,7 @@
package de.twomartens.timetable.bahnApi.model.db
import de.twomartens.timetable.bahnApi.model.Eva
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.types.HourAtDay
import org.bson.types.ObjectId
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id
@@ -8,7 +8,7 @@ import jakarta.xml.bind.annotation.XmlRootElement
@XmlRootElement(name = "stations")
@XmlAccessorType(XmlAccessType.FIELD)
data class BahnStations(
@field:XmlElement(name = "station") var stations: List<BahnStation>
@field:XmlElement(name = "station") var stations: List<BahnStation>
) {
constructor() : this(listOf())
}
@@ -9,22 +9,22 @@ import java.time.LocalDateTime
@XmlAccessorType(XmlAccessType.FIELD)
data class BahnStopEvent(
@field:XmlAttribute(name = "pde") var plannedDistantEndpoint: String,
@field:XmlAttribute(name = "cde") var changedDistantEndpoint: String,
@field:XmlAttribute(name = "pt")
@field:XmlJavaTypeAdapter(value = LocalDateTimeAdapter::class)
var plannedTime: LocalDateTime?,
@field:XmlAttribute(name = "ct")
@field:XmlJavaTypeAdapter(value = LocalDateTimeAdapter::class)
var changedTime: LocalDateTime?,
@field:XmlAttribute(name = "ppth") var plannedPath: String,
@field:XmlAttribute(name = "cpth") var changedPath: String,
@field:XmlAttribute(name = "pp") var plannedPlatform: String,
@field:XmlAttribute(name = "cp") var changedPlatform: String,
@field:XmlAttribute(name = "wings") var wings: String,
@field:XmlAttribute(name = "l") var line: String
@field:XmlAttribute(name = "pde") var plannedDistantEndpoint: String,
@field:XmlAttribute(name = "cde") var changedDistantEndpoint: String,
@field:XmlAttribute(name = "pt")
@field:XmlJavaTypeAdapter(value = LocalDateTimeAdapter::class)
var plannedTime: LocalDateTime?,
@field:XmlAttribute(name = "ct")
@field:XmlJavaTypeAdapter(value = LocalDateTimeAdapter::class)
var changedTime: LocalDateTime?,
@field:XmlAttribute(name = "ppth") var plannedPath: String,
@field:XmlAttribute(name = "cpth") var changedPath: String,
@field:XmlAttribute(name = "pp") var plannedPlatform: String,
@field:XmlAttribute(name = "cp") var changedPlatform: String,
@field:XmlAttribute(name = "wings") var wings: String,
@field:XmlAttribute(name = "l") var line: String
) {
constructor(): this("", "",
constructor() : this("", "",
null,
null,
"", "", "", "",
@@ -6,12 +6,12 @@ import jakarta.xml.bind.annotation.XmlAttribute
@XmlAccessorType(XmlAccessType.FIELD)
data class BahnTripLabel(
@field:XmlAttribute(name = "c") var category: String,
// S: S-Bahn, N: Nahverkehr, F: Fernverkehr
@field:XmlAttribute(name = "f") var filterFlags: String,
@field:XmlAttribute(name = "n") var trainNumber: String,
@field:XmlAttribute(name = "o") var owner: String,
@field:XmlAttribute(name = "t") var tripType: BahnTripType
@field:XmlAttribute(name = "c") var category: String,
// S: S-Bahn, N: Nahverkehr, F: Fernverkehr
@field:XmlAttribute(name = "f") var filterFlags: String,
@field:XmlAttribute(name = "n") var trainNumber: String,
@field:XmlAttribute(name = "o") var owner: String,
@field:XmlAttribute(name = "t") var tripType: BahnTripType
) {
constructor() : this("", "", "", "", BahnTripType.p)
}
@@ -5,7 +5,7 @@ import de.twomartens.timetable.bahnApi.model.dto.BahnStation
import de.twomartens.timetable.bahnApi.model.dto.BahnStations
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
import de.twomartens.timetable.bahnApi.property.BahnApiProperties
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.types.HourAtDay
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpMethod
@@ -23,7 +23,7 @@ class BahnApiService(
fun fetchStations(pattern: String): List<BahnStation> {
val requestEntity = buildRequestEntity<BahnStations>()
val response = restTemplate.exchange(
"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/${pattern}",
"https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/station/${pattern}",
HttpMethod.GET,
requestEntity,
BahnStations::class.java
@@ -6,7 +6,7 @@ import de.twomartens.timetable.bahnApi.model.dto.BahnStation
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
import de.twomartens.timetable.bahnApi.repository.BahnStationRepository
import de.twomartens.timetable.bahnApi.repository.BahnTimetableRepository
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.types.HourAtDay
import org.mapstruct.factory.Mappers
import org.springframework.stereotype.Service
@@ -5,9 +5,9 @@ import de.twomartens.timetable.bahnApi.model.FetchDates
import de.twomartens.timetable.bahnApi.model.TaskFactory
import de.twomartens.timetable.bahnApi.model.db.ScheduledFetchTask
import de.twomartens.timetable.bahnApi.repository.ScheduledFetchTaskRepository
import de.twomartens.timetable.model.base.Hour
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.model.db.TswRoute
import de.twomartens.timetable.types.Hour
import de.twomartens.timetable.types.HourAtDay
import mu.KotlinLogging
import org.springframework.stereotype.Service
import java.time.LocalDate
@@ -6,8 +6,8 @@ import de.twomartens.timetable.bahnApi.repository.ScheduledFetchTaskRepository
import de.twomartens.timetable.bahnApi.tasks.DeleteScheduledTask
import de.twomartens.timetable.bahnApi.tasks.FetchTimetableTask
import de.twomartens.timetable.bahnApi.tasks.StoreTimetableTask
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.property.TimeProperties
import de.twomartens.timetable.types.HourAtDay
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
import org.springframework.stereotype.Service
@@ -3,7 +3,7 @@ package de.twomartens.timetable.bahnApi.tasks
import de.twomartens.timetable.bahnApi.model.Eva
import de.twomartens.timetable.bahnApi.service.BahnApiService
import de.twomartens.timetable.bahnApi.service.TaskScheduler
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.types.HourAtDay
import mu.KotlinLogging
import org.springframework.scheduling.annotation.Async
@@ -2,7 +2,7 @@ package de.twomartens.timetable.bahnApi.tasks
import de.twomartens.timetable.bahnApi.model.dto.BahnTimetable
import de.twomartens.timetable.bahnApi.service.BahnDatabaseService
import de.twomartens.timetable.model.base.HourAtDay
import de.twomartens.timetable.types.HourAtDay
import mu.KotlinLogging
import org.springframework.scheduling.annotation.Async
@@ -10,5 +10,5 @@ import org.springframework.context.annotation.Configuration
@Configuration
@EnableConfigurationProperties(RestTemplateTimeoutProperties::class, ServiceProperties::class,
StatusProbeProperties::class, TimeProperties::class, BahnApiProperties::class)
StatusProbeProperties::class, TimeProperties::class, BahnApiProperties::class)
open class PropertyConfiguration
@@ -1,9 +1,9 @@
package de.twomartens.timetable.controller
import de.twomartens.timetable.bahnApi.service.ScheduledTaskService
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.model.db.UserId
import de.twomartens.timetable.repository.TswRouteRepository
import de.twomartens.timetable.types.NonEmptyString
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.responses.ApiResponse
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.PositiveInteger
import de.twomartens.timetable.types.PositiveInteger
@JvmInline
value class CoachCapacity(val capacity: PositiveInteger)
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
@JvmInline
value class CountryCode(val countryCode: NonEmptyString) {
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import kotlin.time.Duration
data class Depot(
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import org.bson.types.ObjectId
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.PositiveInteger
import de.twomartens.timetable.types.PositiveInteger
@JvmInline
value class FormationId(val id: PositiveInteger)
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import kotlin.time.Duration
data class Portal(
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
@JvmInline
value class Section(val name: NonEmptyString)
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
@JvmInline
value class StationId(val id: NonEmptyString) {
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.PositiveInteger
import de.twomartens.timetable.types.PositiveInteger
@JvmInline
value class TrackId(val id: PositiveInteger)
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import org.bson.types.ObjectId
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
import org.bson.types.ObjectId
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id
@@ -1,6 +1,6 @@
package de.twomartens.timetable.model.db
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.types.NonEmptyString
@JvmInline
value class UserId(val id: NonEmptyString)
@@ -1,8 +1,8 @@
package de.twomartens.timetable.repository
import de.twomartens.timetable.model.base.NonEmptyString
import de.twomartens.timetable.model.db.TswRoute
import de.twomartens.timetable.model.db.UserId
import de.twomartens.timetable.types.NonEmptyString
import org.bson.types.ObjectId
import org.springframework.data.mongodb.repository.MongoRepository
@@ -6,8 +6,8 @@ import org.springframework.web.filter.ForwardedHeaderFilter
@Configuration
open class FilterConfiguration {
@Bean
open fun forwardedFilter(): ForwardedHeaderFilter {
return ForwardedHeaderFilter()
}
@Bean
open fun forwardedFilter(): ForwardedHeaderFilter {
return ForwardedHeaderFilter()
}
}
@@ -8,13 +8,13 @@ import java.time.Clock
@Configuration
open class InterceptorConfiguration {
@Bean
open fun loggingInterceptorRest(clock: Clock): LoggingInterceptorRest {
return LoggingInterceptorRest(clock)
}
@Bean
open fun loggingInterceptorRest(clock: Clock): LoggingInterceptorRest {
return LoggingInterceptorRest(clock)
}
@Bean
open fun headerInterceptorRest(): HeaderInterceptorRest {
return HeaderInterceptorRest()
}
@Bean
open fun headerInterceptorRest(): HeaderInterceptorRest {
return HeaderInterceptorRest()
}
}
@@ -12,28 +12,28 @@ import org.springframework.context.annotation.Configuration
@SecurityScheme(name = "bearerAuth", type = SecuritySchemeType.HTTP, scheme = "bearer", bearerFormat = "JWT")
@SecurityScheme(
name = "oauth2",
type = SecuritySchemeType.OAUTH2,
flows = OAuthFlows(
implicit = OAuthFlow(
authorizationUrl = "https://id.2martens.de/realms/2martens/protocol/openid-connect/auth",
tokenUrl = "https://id.2martens.de/realms/2martens/protocol/openid-connect/token"
name = "oauth2",
type = SecuritySchemeType.OAUTH2,
flows = OAuthFlows(
implicit = OAuthFlow(
authorizationUrl = "https://id.2martens.de/realms/2martens/protocol/openid-connect/auth",
tokenUrl = "https://id.2martens.de/realms/2martens/protocol/openid-connect/token"
)
)
)
)
@Configuration
open class OpenApiConfiguration {
@Bean
open fun customOpenAPI(
@Value("\${openapi.description}") apiDescription: String,
@Value("\${openapi.version}") apiVersion: String, @Value("\${openapi.title}") apiTitle: String
@Value("\${openapi.description}") apiDescription: String,
@Value("\${openapi.version}") apiVersion: String, @Value("\${openapi.title}") apiTitle: String
): OpenAPI {
return OpenAPI()
.info(
Info()
.title(apiTitle)
.version(apiVersion)
.description(apiDescription)
)
.info(
Info()
.title(apiTitle)
.version(apiVersion)
.description(apiDescription)
)
}
}
@@ -1,8 +1,8 @@
package de.twomartens.timetable.support.configuration
import de.twomartens.timetable.property.RestTemplateTimeoutProperties
import de.twomartens.timetable.support.interceptor.HeaderInterceptorRest
import de.twomartens.timetable.support.interceptor.LoggingInterceptorRest
import de.twomartens.timetable.property.RestTemplateTimeoutProperties
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@@ -17,10 +17,10 @@ open class RestTemplateConfiguration {
restTemplateTimeoutProperties: RestTemplateTimeoutProperties
): RestTemplate {
return RestTemplateBuilder()
.additionalInterceptors(headerInterceptorRest, loggingInterceptor)
.setConnectTimeout(restTemplateTimeoutProperties.connectionRestTemplateTimeoutInMillis)
.setReadTimeout(restTemplateTimeoutProperties.readTimeoutRestTemplateInMillis)
.build()
.additionalInterceptors(headerInterceptorRest, loggingInterceptor)
.setConnectTimeout(restTemplateTimeoutProperties.connectionRestTemplateTimeoutInMillis)
.setReadTimeout(restTemplateTimeoutProperties.readTimeoutRestTemplateInMillis)
.build()
}
@Bean("restTemplateRestHealthIndicator")
@@ -29,9 +29,9 @@ open class RestTemplateConfiguration {
restTemplateTimeoutProperties: RestTemplateTimeoutProperties
): RestTemplate {
return RestTemplateBuilder()
.additionalInterceptors(headerInterceptorRest)
.setConnectTimeout(restTemplateTimeoutProperties.connectionRestHealthIndicatorTimeoutInMillis)
.setReadTimeout(restTemplateTimeoutProperties.readTimeoutRestHealthIndicatorInMillis)
.build()
.additionalInterceptors(headerInterceptorRest)
.setConnectTimeout(restTemplateTimeoutProperties.connectionRestHealthIndicatorTimeoutInMillis)
.setReadTimeout(restTemplateTimeoutProperties.readTimeoutRestHealthIndicatorInMillis)
.build()
}
}
@@ -18,8 +18,8 @@ open class StatusProbeConfiguration(private val clock: Clock) {
@Bean
open fun testStatusProbe(statusProbeLogger: StatusProbeLogger): StatusProbe {
return CountBasedStatusProbe(
1,
clock, StatusProbeCriticality.K1, "testStatusProbe", statusProbeLogger
1,
clock, StatusProbeCriticality.K1, "testStatusProbe", statusProbeLogger
)
}
}
@@ -34,12 +34,12 @@ open class WebSecurityConfiguration {
@Throws(Exception::class)
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf { it.disable() }
.authorizeHttpRequests { it.requestMatchers(*PERMITTED_PATHS.toTypedArray<String>()).permitAll() }
.authorizeHttpRequests { it.requestMatchers(HttpMethod.OPTIONS).permitAll() }
.authorizeHttpRequests { it.anyRequest().authenticated() }
.oauth2ResourceServer { obj: OAuth2ResourceServerConfigurer<HttpSecurity?> -> obj.jwt(Customizer.withDefaults()) }
.addFilterAfter(createPolicyEnforcerFilter(), BearerTokenAuthenticationFilter::class.java)
.csrf { it.disable() }
.authorizeHttpRequests { it.requestMatchers(*PERMITTED_PATHS.toTypedArray<String>()).permitAll() }
.authorizeHttpRequests { it.requestMatchers(HttpMethod.OPTIONS).permitAll() }
.authorizeHttpRequests { it.anyRequest().authenticated() }
.oauth2ResourceServer { obj: OAuth2ResourceServerConfigurer<HttpSecurity?> -> obj.jwt(Customizer.withDefaults()) }
.addFilterAfter(createPolicyEnforcerFilter(), BearerTokenAuthenticationFilter::class.java)
return http.build()
}
@@ -53,7 +53,7 @@ open class WebSecurityConfiguration {
override fun resolve(request: HttpRequest): PolicyEnforcerConfig {
return try {
val policyEnforcerConfig = JsonSerialization.readValue(
javaClass.getResourceAsStream("/policy-enforcer.json"), PolicyEnforcerConfig::class.java
javaClass.getResourceAsStream("/policy-enforcer.json"), PolicyEnforcerConfig::class.java
)
policyEnforcerConfig.credentials = mapOf(Pair("secret", clientSecret))
if (request.method == HttpMethod.OPTIONS.name()) {
@@ -72,12 +72,12 @@ open class WebSecurityConfiguration {
companion object {
private val PERMITTED_PATHS: Collection<String> = listOf(
"/timetable/v1/healthCheck",
"/actuator/**",
"/timetable/v1/doc/**",
"/timetable/v1/api-docs/**",
"/error",
"/timetable/version",
"/timetable/v1/healthCheck",
"/actuator/**",
"/timetable/v1/doc/**",
"/timetable/v1/api-docs/**",
"/error",
"/timetable/version",
)
private val PATHS = buildPathConfigs()
@@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
@ControllerAdvice(annotations = [RestController::class])
class ExceptionController: ResponseEntityExceptionHandler() {
class ExceptionController : ResponseEntityExceptionHandler() {
@ExceptionHandler(HttpStatusException::class)
fun handleException(e: HttpStatusException): ResponseEntity<ErrorMessage> {
if (e.cause != null) {
@@ -65,7 +65,8 @@ class VersionHtmlController {
JarFile(jarFileName).use { jarFile ->
val entry = jarFile.getEntry(JarFile.MANIFEST_NAME)
jarFile.getInputStream(entry).use { `in` ->
return String(`in`.readAllBytes(), StandardCharsets.UTF_8).lines().toList() }
return String(`in`.readAllBytes(), StandardCharsets.UTF_8).lines().toList()
}
}
} catch (ignored: FileNotFoundException) {
// do nothing if manifest file is not available
@@ -2,5 +2,5 @@ package de.twomartens.timetable.support.exception
import org.springframework.http.HttpStatus
class HttpStatusException(val status: HttpStatus, message: String, cause: Throwable?):
class HttpStatusException(val status: HttpStatus, message: String, cause: Throwable?) :
RuntimeException(message, cause)
@@ -8,7 +8,7 @@ import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.InputStream
class BufferingClientHttpResponseWrapper(private val response: ClientHttpResponse): ClientHttpResponse {
class BufferingClientHttpResponseWrapper(private val response: ClientHttpResponse) : ClientHttpResponse {
private var body: ByteArray? = null
@Throws(IOException::class)
@@ -44,8 +44,8 @@ class HeaderInterceptorRest : HeaderInterceptor(), HandlerInterceptor, ClientHtt
// ClientHttpRequestInterceptor
@Throws(IOException::class)
override fun intercept(
request: HttpRequest, body: ByteArray,
execution: ClientHttpRequestExecution
request: HttpRequest, body: ByteArray,
execution: ClientHttpRequestExecution
): ClientHttpResponse {
request.headers.add(HEADER_FIELD_TRACE_ID, getTraceId())
val requestType = getRequestType()
@@ -63,8 +63,8 @@ class HeaderInterceptorRest : HeaderInterceptor(), HandlerInterceptor, ClientHtt
// HandlerInterceptor
override fun preHandle(
request: HttpServletRequest, response: HttpServletResponse,
handler: Any
request: HttpServletRequest, response: HttpServletResponse,
handler: Any
): Boolean {
val traceId = extractTraceId(request)
val requestType = extractRequestType(request)
@@ -35,14 +35,14 @@ class LoggingInterceptorRest(
@Throws(IOException::class, ServletException::class)
override fun doFilter(
request: ServletRequest, response: ServletResponse,
chain: FilterChain
request: ServletRequest, response: ServletResponse,
chain: FilterChain
) {
val requestTime = ZonedDateTime.now(clock)
val httpRequest = request as HttpServletRequest
val requestWrapper = ContentCachingRequestWrapper(httpRequest)
val responseWrapper = ContentCachingResponseWrapper(
(response as HttpServletResponse)
(response as HttpServletResponse)
)
var fullResponseBytes: ByteArray? = null
var throwable: Throwable? = null
@@ -51,8 +51,8 @@ class LoggingInterceptorRest(
try {
try {
chain.doFilter(
if (requestLogBehaviour != FieldLogBehaviour.NEVER) requestWrapper else httpRequest,
responseWrapper
if (requestLogBehaviour != FieldLogBehaviour.NEVER) requestWrapper else httpRequest,
responseWrapper
)
if (responseLogBehaviour != FieldLogBehaviour.NEVER) {
fullResponseBytes = responseWrapper.contentAsByteArray
@@ -68,58 +68,58 @@ class LoggingInterceptorRest(
try {
val responseSize = responseWrapper.contentSize
val responseHeaders = extractHeaders(
headerNames = responseWrapper.headerNames.iterator()
headerNames = responseWrapper.headerNames.iterator()
) { responseWrapper.getHeaders(it).iterator() }
if (
(responseLogBehaviour == FieldLogBehaviour.ONLY_ON_ERROR && isError(httpStatusCode)
|| responseLogBehaviour == FieldLogBehaviour.ALWAYS)
&& fullResponseBytes != null
(responseLogBehaviour == FieldLogBehaviour.ONLY_ON_ERROR && isError(httpStatusCode)
|| responseLogBehaviour == FieldLogBehaviour.ALWAYS)
&& fullResponseBytes != null
) {
responseBody = String(
fullResponseBytes,
determineResponseEncoding()
fullResponseBytes,
determineResponseEncoding()
)
}
val query = if (httpRequest.queryString != null) "?${httpRequest.queryString}" else ""
val requestUrl = URL("${httpRequest.requestURL}$query")
val requestHeaders = extractHeaders(
headerNames = httpRequest.headerNames.asIterator(),
ignoreList = listOf("authorization")) {
headerNames = httpRequest.headerNames.asIterator(),
ignoreList = listOf("authorization")) {
httpRequest.getHeaders(it).asIterator()
}
var requestBody: String? = null
var businessType: String? = null
if (requestLogBehaviour == FieldLogBehaviour.ONLY_ON_ERROR && isError(httpStatusCode)
|| requestLogBehaviour == FieldLogBehaviour.ALWAYS
|| requestLogBehaviour == FieldLogBehaviour.ALWAYS
) {
val fullRequestBytes = requestWrapper.contentAsByteArray
requestBody = String(fullRequestBytes, determineRequestEncoding())
businessType = determineBusinessType()
}
log(
LogMessage(
requestHeaders = requestHeaders,
responseHeaders = responseHeaders,
url = requestUrl,
method = httpRequest.method,
requestMimeType = typeToString(request.getContentType()),
responseMimeType = typeToString(response.getContentType()),
requestBody = requestBody,
responseBody = responseBody,
requestSize = httpRequest.contentLength,
responseSize = responseSize,
httpStatus = httpStatusCode,
direction = DIRECTION_IN,
requestTime = requestTime,
responseTime = ZonedDateTime.now(clock),
traceId = HeaderInterceptor.getTraceId(),
requestType = HeaderInterceptor.getRequestType(),
businessType = businessType,
throwable = throwable
)
LogMessage(
requestHeaders = requestHeaders,
responseHeaders = responseHeaders,
url = requestUrl,
method = httpRequest.method,
requestMimeType = typeToString(request.getContentType()),
responseMimeType = typeToString(response.getContentType()),
requestBody = requestBody,
responseBody = responseBody,
requestSize = httpRequest.contentLength,
responseSize = responseSize,
httpStatus = httpStatusCode,
direction = DIRECTION_IN,
requestTime = requestTime,
responseTime = ZonedDateTime.now(clock),
traceId = HeaderInterceptor.getTraceId(),
requestType = HeaderInterceptor.getRequestType(),
businessType = businessType,
throwable = throwable
)
)
val interceptorCloseables =
request.getAttribute(HeaderInterceptorRest.CLASS_NAME) as HeaderInterceptor.InterceptorCloseables
request.getAttribute(HeaderInterceptorRest.CLASS_NAME) as HeaderInterceptor.InterceptorCloseables
interceptorCloseables.close()
} catch (e: java.lang.RuntimeException) {
log.error(e.toString(), e)
@@ -133,8 +133,8 @@ class LoggingInterceptorRest(
@Throws(IOException::class)
override fun intercept(
request: HttpRequest,
requestBytes: ByteArray, execution: ClientHttpRequestExecution
request: HttpRequest,
requestBytes: ByteArray, execution: ClientHttpRequestExecution
): ClientHttpResponse {
val requestTime = ZonedDateTime.now(clock)
var responseSize = 0
@@ -147,7 +147,7 @@ class LoggingInterceptorRest(
var businessType: String? = null
return try {
val result = BufferingClientHttpResponseWrapper(
execution.execute(request, requestBytes)
execution.execute(request, requestBytes)
)
val responseBytes = StreamUtils.copyToByteArray(result.getBody())
responseSize = responseBytes.size
@@ -155,11 +155,11 @@ class LoggingInterceptorRest(
responseMediaType = result.headers.getContentType()
httpStatusCode = result.statusCode.value()
if (responseLogBehaviour == FieldLogBehaviour.ONLY_ON_ERROR && isError(httpStatusCode)
|| responseLogBehaviour == FieldLogBehaviour.ALWAYS
|| responseLogBehaviour == FieldLogBehaviour.ALWAYS
) {
responseBody = String(
responseBytes,
determineRequestEncoding()
responseBytes,
determineRequestEncoding()
)
}
result
@@ -171,35 +171,35 @@ class LoggingInterceptorRest(
val url = request.uri.toURL()
val requestHeaders: Map<String, Collection<String>> = extractHeaders(request.headers)
if (requestLogBehaviour == FieldLogBehaviour.ONLY_ON_ERROR && isError(httpStatusCode)
|| requestLogBehaviour == FieldLogBehaviour.ALWAYS
|| requestLogBehaviour == FieldLogBehaviour.ALWAYS
) {
requestBody = String(
requestBytes,
determineRequestEncoding()
requestBytes,
determineRequestEncoding()
)
businessType = determineBusinessType()
}
log(
LogMessage(
requestHeaders = requestHeaders,
responseHeaders = responseHeaders,
url = url,
method = request.method.name(),
requestMimeType = typeToString(request.headers.getContentType()),
requestBody = requestBody,
responseBody = responseBody,
responseMimeType = typeToString(responseMediaType!!),
requestSize = requestBytes.size,
responseSize = responseSize,
httpStatus = httpStatusCode,
direction = DIRECTION_OUT,
requestTime = requestTime,
responseTime = ZonedDateTime.now(clock),
businessType = businessType,
throwable = throwable,
traceId = HeaderInterceptorRest.extractTraceId(request),
requestType = HeaderInterceptorRest.extractRequestType(request),
)
LogMessage(
requestHeaders = requestHeaders,
responseHeaders = responseHeaders,
url = url,
method = request.method.name(),
requestMimeType = typeToString(request.headers.getContentType()),
requestBody = requestBody,
responseBody = responseBody,
responseMimeType = typeToString(responseMediaType!!),
requestSize = requestBytes.size,
responseSize = responseSize,
httpStatus = httpStatusCode,
direction = DIRECTION_OUT,
requestTime = requestTime,
responseTime = ZonedDateTime.now(clock),
businessType = businessType,
throwable = throwable,
traceId = HeaderInterceptorRest.extractTraceId(request),
requestType = HeaderInterceptorRest.extractRequestType(request),
)
)
} catch (e: java.lang.RuntimeException) {
log.error(e.toString(), e)
@@ -210,18 +210,18 @@ class LoggingInterceptorRest(
private fun log(logMessage: LogMessage) {
val stringMapMessage = StringMapMessage()
addLogString(
stringMapMessage, PARAM_REQUEST_HEADERS,
toHeaderString(logMessage.requestHeaders)
stringMapMessage, PARAM_REQUEST_HEADERS,
toHeaderString(logMessage.requestHeaders)
)
addLogString(
stringMapMessage, PARAM_RESPONSE_HEADERS,
toHeaderString(logMessage.responseHeaders)
stringMapMessage, PARAM_RESPONSE_HEADERS,
toHeaderString(logMessage.responseHeaders)
)
addLogString(stringMapMessage, PARAM_URL_FULL, logMessage.url.toString())
addLogString(stringMapMessage, PARAM_URL_DOMAIN, logMessage.url.host)
addLogString(
stringMapMessage, PARAM_URL_EXTENSION,
extractExtension(logMessage.url.path)
stringMapMessage, PARAM_URL_EXTENSION,
extractExtension(logMessage.url.path)
)
addLogString(stringMapMessage, PARAM_URL_PATH, logMessage.url.path)
addLogString(stringMapMessage, PARAM_URL_PORT, logMessage.url.port.toString())
@@ -229,8 +229,8 @@ class LoggingInterceptorRest(
addLogString(stringMapMessage, PARAM_URL_QUERY, logMessage.url.query)
addLogString(stringMapMessage, PARAM_REQUEST_METHOD, logMessage.method)
addLogString(
stringMapMessage, PARAM_REQUEST_REFERER,
getHeader(logMessage.requestHeaders, HttpHeaders.REFERER)
stringMapMessage, PARAM_REQUEST_REFERER,
getHeader(logMessage.requestHeaders, HttpHeaders.REFERER)
)
addLogString(stringMapMessage, PARAM_REQUEST_MIMETYPE, logMessage.requestMimeType)
addLogString(stringMapMessage, PARAM_RESPONSE_MIMETYPE, logMessage.responseMimeType)
@@ -240,21 +240,21 @@ class LoggingInterceptorRest(
addLogString(stringMapMessage, PARAM_DIRECTION, logMessage.direction)
addLogString(stringMapMessage, PARAM_PROTOCOL, PROTOCOL_NAME)
addLogString(
stringMapMessage, PARAM_REQUEST_TIME,
logMessage.requestTime.format(DATE_TIME_FORMATTER)
stringMapMessage, PARAM_REQUEST_TIME,
logMessage.requestTime.format(DATE_TIME_FORMATTER)
)
addLogString(
stringMapMessage, PARAM_RESPONSE_TIME,
logMessage.responseTime.format(DATE_TIME_FORMATTER)
stringMapMessage, PARAM_RESPONSE_TIME,
logMessage.responseTime.format(DATE_TIME_FORMATTER)
)
addLogString(
stringMapMessage,
PARAM_DURATION,
getDurationBetweenRequestAndResponseTime(logMessage).toNanos().toString()
stringMapMessage,
PARAM_DURATION,
getDurationBetweenRequestAndResponseTime(logMessage).toNanos().toString()
)
addLogString(
stringMapMessage, PARAM_USER_AGENT,
getHeader(logMessage.requestHeaders, HttpHeaders.USER_AGENT)
stringMapMessage, PARAM_USER_AGENT,
getHeader(logMessage.requestHeaders, HttpHeaders.USER_AGENT)
)
addLogString(stringMapMessage, HeaderInterceptor.LOGGER_TRACE_ID, logMessage.traceId)
addLogString(stringMapMessage, HeaderInterceptor.LOGGER_REQTYPE_ID, logMessage.requestType)
@@ -270,9 +270,9 @@ class LoggingInterceptorRest(
private fun getHeader(headers: Map<String, Collection<String?>>, headerKey: String): String? {
return headers.entries.asSequence()
.filter { it.key.equals(headerKey, ignoreCase = true) }
.flatMap { it.value.asSequence() }
.firstOrNull()
.filter { it.key.equals(headerKey, ignoreCase = true) }
.flatMap { it.value.asSequence() }
.firstOrNull()
}
private fun addLogString(stringMapMessage: StringMapMessage, key: String, value: String?) {
@@ -340,9 +340,9 @@ class LoggingInterceptorRest(
const val PARAM_PROTOCOL = "network.protocol"
private fun extractHeaders(
headerNames: Iterator<String>,
ignoreList: List<String> = emptyList(),
headerValuesSupplier: Function<String, Iterator<String>>
headerNames: Iterator<String>,
ignoreList: List<String> = emptyList(),
headerValuesSupplier: Function<String, Iterator<String>>
): Map<String, MutableCollection<String>> {
val requestHeaders: MutableMap<String, MutableCollection<String>> = mutableMapOf()
while (headerNames.hasNext()) {
@@ -367,12 +367,12 @@ class LoggingInterceptorRest(
private fun toHeaderString(headerMap: Map<String, Collection<String>>): String {
return headerMap.entries.asSequence()
.flatMap { entry ->
val key = entry.key
entry.value.asSequence().map { Pair(key, it) }
}
.map { "${it.first}=${it.second}" }
.joinToString(separator = ",")
.flatMap { entry ->
val key = entry.key
entry.value.asSequence().map { Pair(key, it) }
}
.map { "${it.first}=${it.second}" }
.joinToString(separator = ",")
}
private fun typeToString(contentType: String?): String? {
@@ -408,24 +408,24 @@ class LoggingInterceptorRest(
}
private data class LogMessage(
val requestHeaders: Map<String, Collection<String>>,
val responseHeaders: Map<String, Collection<String>>,
val url: URL,
val method: String,
val requestMimeType: String?,
val responseMimeType: String?,
val requestBody: String?,
val responseBody: String?,
val requestSize: Int,
val responseSize: Int,
val httpStatus: Int,
val direction: String,
val requestTime: ZonedDateTime,
val responseTime: ZonedDateTime,
val traceId: String?,
val requestType: String?,
val businessType: String?,
val throwable: Throwable?
val requestHeaders: Map<String, Collection<String>>,
val responseHeaders: Map<String, Collection<String>>,
val url: URL,
val method: String,
val requestMimeType: String?,
val responseMimeType: String?,
val requestBody: String?,
val responseBody: String?,
val requestSize: Int,
val responseSize: Int,
val httpStatus: Int,
val direction: String,
val requestTime: ZonedDateTime,
val responseTime: ZonedDateTime,
val traceId: String?,
val requestType: String?,
val businessType: String?,
val throwable: Throwable?
)
enum class FieldLogBehaviour {
@@ -11,8 +11,8 @@ import java.time.Duration
import java.util.*
abstract class AbstractHealthIndicator(
private val clock: Clock,
private val preparable: Preparable
private val clock: Clock,
private val preparable: Preparable
) : HealthIndicator {
private val logStatusDownMessage = "health indicator '${indicatorName()}' invoked with status '${Status.DOWN.code}'"
@@ -67,8 +67,8 @@ abstract class AbstractHealthIndicator(
private fun indicatorName(): String {
return this.javaClass.getSimpleName()
.replace("HealthIndicator", "")
.lowercase(Locale.getDefault())
.replace("HealthIndicator", "")
.lowercase(Locale.getDefault())
}
companion object {
@@ -1,7 +1,7 @@
package de.twomartens.timetable.support.monitoring.actuator
import de.twomartens.timetable.support.interceptor.HeaderInterceptorRest
import de.twomartens.timetable.property.ServiceProperties
import de.twomartens.timetable.support.interceptor.HeaderInterceptorRest
import org.springframework.boot.actuate.health.Health
import org.springframework.boot.actuate.health.HealthIndicator
import org.springframework.boot.actuate.health.Status
@@ -7,11 +7,11 @@ import java.time.Duration
interface ScheduledStatusProbe {
fun runScheduledTask()
fun scheduleTask(
threadPoolTaskScheduler: ThreadPoolTaskScheduler,
schedulePeriod: Duration
threadPoolTaskScheduler: ThreadPoolTaskScheduler,
schedulePeriod: Duration
) {
val periodicTrigger = PeriodicTrigger(
Duration.ofSeconds(schedulePeriod.toSeconds())
Duration.ofSeconds(schedulePeriod.toSeconds())
)
threadPoolTaskScheduler.schedule(periodicTrigger) { runScheduledTask() }
}
@@ -19,8 +19,8 @@ class StatusProbeLogger internal constructor(private val clock: Clock, private v
}
fun logStatusChange(
name: String, message: String?, status: Status, lastStatusChange: ZonedDateTime?,
throwable: Throwable?
name: String, message: String?, status: Status, lastStatusChange: ZonedDateTime?,
throwable: Throwable?
) {
var probeIdent = getProbeIdent(name)
if (probeIdent == null) {
@@ -32,8 +32,8 @@ class StatusProbeLogger internal constructor(private val clock: Clock, private v
private fun getProbeIdent(name: String): ProbeIdent? {
return statusProbeToStatus.keys.asSequence()
.filter { it.name == name }
.firstOrNull()
.filter { it.name == name }
.firstOrNull()
}
private fun createLog(message: String?, lastStatusChange: ZonedDateTime?, throwable: Throwable?) {
@@ -42,7 +42,7 @@ class StatusProbeLogger internal constructor(private val clock: Clock, private v
val criticality = overallCriticality
if (Status.UP == overallStatus) {
commLog.info(
MARKER, StringMapMessage()
MARKER, StringMapMessage()
.with(LABEL_CRITICALITY, criticality)
.with(LABEL_STATUS, overallStatus)
.with(LABEL_MESSAGE, cleanedMessage)
@@ -50,7 +50,7 @@ class StatusProbeLogger internal constructor(private val clock: Clock, private v
)
} else {
commLog.error(
MARKER, StringMapMessage()
MARKER, StringMapMessage()
.with(LABEL_CRITICALITY, criticality)
.with(LABEL_STATUS, overallStatus)
.with(LABEL_MESSAGE, cleanedMessage)
@@ -63,8 +63,8 @@ class StatusProbeLogger internal constructor(private val clock: Clock, private v
private val overallCriticality: StatusProbeCriticality
get() {
val crits = statusProbeToStatus.keys.asSequence()
.map { it.criticality }
.toList()
.map { it.criticality }
.toList()
return if (crits.contains(StatusProbeCriticality.K1)) StatusProbeCriticality.K1
else if (crits.contains(StatusProbeCriticality.K2)) StatusProbeCriticality.K2
else StatusProbeCriticality.K3
@@ -77,9 +77,9 @@ class StatusProbeLogger internal constructor(private val clock: Clock, private v
private val reason: String
get() {
val probesDown = statusProbeToStatus.entries.asSequence()
.filter { it.value == Status.DOWN }
.map { it.key }
.toList()
.filter { it.value == Status.DOWN }
.map { it.key }
.toList()
val reasonK1 = getDownStatusProbes(probesDown, StatusProbeCriticality.K1)
val reasonK2 = getDownStatusProbes(probesDown, StatusProbeCriticality.K2)
val reasonK3 = getDownStatusProbes(probesDown, StatusProbeCriticality.K3)
@@ -88,9 +88,9 @@ class StatusProbeLogger internal constructor(private val clock: Clock, private v
private fun getDownStatusProbes(probesDown: List<ProbeIdent>, criticality: StatusProbeCriticality): String {
val downProbeNames = probesDown.asSequence()
.filter { it.criticality == criticality }
.map { it.name }
.toList()
.filter { it.criticality == criticality }
.map { it.name }
.toList()
return if (downProbeNames.isNotEmpty()) {
"$criticality failed: ${downProbeNames.joinToString(separator = ",")}\n"
} else ""
@@ -8,8 +8,8 @@ import java.time.temporal.ChronoUnit
@ConfigurationProperties(prefix = "resttemplate.timeout")
data class RestTemplateTimeoutProperties @ConstructorBinding constructor(
@DurationUnit(ChronoUnit.MILLIS) val readTimeoutRestHealthIndicatorInMillis: Duration,
@DurationUnit(ChronoUnit.MILLIS) val connectionRestHealthIndicatorTimeoutInMillis: Duration,
@DurationUnit(ChronoUnit.MILLIS) val readTimeoutRestTemplateInMillis: Duration,
@DurationUnit(ChronoUnit.MILLIS) val connectionRestTemplateTimeoutInMillis: Duration
@DurationUnit(ChronoUnit.MILLIS) val readTimeoutRestHealthIndicatorInMillis: Duration,
@DurationUnit(ChronoUnit.MILLIS) val connectionRestHealthIndicatorTimeoutInMillis: Duration,
@DurationUnit(ChronoUnit.MILLIS) val readTimeoutRestTemplateInMillis: Duration,
@DurationUnit(ChronoUnit.MILLIS) val connectionRestTemplateTimeoutInMillis: Duration
)
@@ -5,7 +5,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.ConstructorBinding
import org.springframework.boot.convert.DurationUnit
import org.springframework.cloud.context.config.annotation.RefreshScope
import org.springframework.context.annotation.Configuration
import java.time.Duration
import java.time.temporal.ChronoUnit
@@ -13,8 +12,8 @@ import java.time.temporal.ChronoUnit
@ConfigurationProperties(prefix = "de.twomartens.timetable.statusprobe")
@Schema(description = "Properties, to configure this Application")
data class StatusProbeProperties @ConstructorBinding constructor(
@DurationUnit(ChronoUnit.SECONDS) val scheduleDuration: Duration,
@DurationUnit(ChronoUnit.MINUTES) val maxKafkaFailureDuration: Duration,
val maxBlobFailureCount: Int,
val maxFailurePercent: Int
@DurationUnit(ChronoUnit.SECONDS) val scheduleDuration: Duration,
@DurationUnit(ChronoUnit.MINUTES) val maxKafkaFailureDuration: Duration,
val maxBlobFailureCount: Int,
val maxFailurePercent: Int
)
@@ -9,6 +9,6 @@ import java.time.ZoneId
@RefreshScope
@ConfigurationProperties(prefix = "time")
@Schema(description = "Properties to configure time")
data class TimeProperties @ConstructorBinding constructor(
val defaultTimeZone: ZoneId
data class TimeProperties @ConstructorBinding constructor(
val defaultTimeZone: ZoneId
)
+1
View File
@@ -1,6 +1,7 @@
val projectname: String = providers.gradleProperty("projectname").get()
rootProject.name = projectname
include("common")
include("server")
include("deploy")