diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6c4cd55..e355f4e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,10 +14,10 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Run Lint diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index eb68a99..67a5866 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -20,10 +20,10 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Publish release diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index 22ac43c..844c752 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -20,10 +20,10 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Publish snapshot diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e53fae..3642a1e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,10 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Run Lint diff --git a/README.md b/README.md index db4fa14..fa3aa39 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,18 @@ ## 와플스튜디오 스프링 라이브러리 - 와플스튜디오에서 사용하는 스프링 라이브러리 모음 +## 배포된 라이브러리 위치 +- AWS CodeArtifact +- 리포지토리 URL: `https://wafflestudio-405906814034.d.codeartifact.ap-northeast-1.amazonaws.com/maven/spring-waffle/` + +## Compatibility +- version 2.0.0 ~ + - JDK 21 이상 + - spring boot 4.0 이상 +- version 1.0.0 ~ 1.0.4 + - JDK 17 이상 + - spring boot 3.x + ## 사용법 ### AWS 설정 - AWS 콘솔에 로그인 diff --git a/build.gradle.kts b/build.gradle.kts index 61aded4..25e900c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,18 +1,19 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.io.FileInputStream import java.io.FileOutputStream import java.util.Properties plugins { - id("org.springframework.boot") version "3.2.4" apply false - id("io.spring.dependency-management") version "1.1.4" - kotlin("jvm") version "1.8.0" - kotlin("plugin.spring") version "1.8.0" + id("org.springframework.boot") version "4.0.1" apply false + kotlin("jvm") version "2.2.0" + kotlin("plugin.spring") version "2.2.0" id("org.jlleitschuh.gradle.ktlint") version "12.2.0" id("maven-publish") } -java.sourceCompatibility = JavaVersion.VERSION_17 +java.sourceCompatibility = JavaVersion.VERSION_21 +java.targetCompatibility = JavaVersion.VERSION_21 allprojects { repositories { @@ -32,13 +33,9 @@ allprojects { withJavadocJar() } - dependencyManagement { - imports { - mavenBom("org.springframework.boot:spring-boot-dependencies:3.2.4") - } - } - dependencies { + implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + implementation(kotlin("stdlib")) implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core") @@ -73,9 +70,9 @@ allprojects { } tasks.withType { - kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = "17" + compilerOptions { + freeCompilerArgs.add("-Xjsr305=strict") + jvmTarget.set(JvmTarget.JVM_21) } } @@ -84,7 +81,7 @@ allprojects { } } -task("updateVersion") { +tasks.register("updateVersion") { properties["releaseVersion"]?.let { releaseVersion -> val newSnapshotVersion = (releaseVersion as String).split(".").let { diff --git a/gradle.properties b/gradle.properties index 6dc6b6a..e19e9da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ #Sun May 04 17:42:48 UTC 2025 kotlin.code.style=official -version=1.1.0-SNAPSHOT +version=2.0.0 group=com.wafflestudio.spring diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661..ac57dd1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/spring-boot-starter-waffle-secret-manager/src/main/kotlin/com/wafflestudio/spring/secretsmanager/config/SecretsManagerEnvironmentPostProcessor.kt b/spring-boot-starter-waffle-secret-manager/src/main/kotlin/com/wafflestudio/spring/secretsmanager/config/SecretsManagerEnvironmentPostProcessor.kt index 1e2c27f..12c315e 100644 --- a/spring-boot-starter-waffle-secret-manager/src/main/kotlin/com/wafflestudio/spring/secretsmanager/config/SecretsManagerEnvironmentPostProcessor.kt +++ b/spring-boot-starter-waffle-secret-manager/src/main/kotlin/com/wafflestudio/spring/secretsmanager/config/SecretsManagerEnvironmentPostProcessor.kt @@ -2,8 +2,8 @@ package com.wafflestudio.spring.secretsmanager.config import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue +import org.springframework.boot.EnvironmentPostProcessor import org.springframework.boot.SpringApplication -import org.springframework.boot.env.EnvironmentPostProcessor import org.springframework.core.env.ConfigurableEnvironment import org.springframework.core.env.MapPropertySource import software.amazon.awssdk.regions.Region @@ -18,6 +18,10 @@ class SecretsManagerEnvironmentPostProcessor : EnvironmentPostProcessor { environment: ConfigurableEnvironment, application: SpringApplication, ) { + val isAotProcessing = environment.getProperty("spring.aot.processing", Boolean::class.java, false) + if (isAotProcessing) { + return + } val secretNamesProperty = environment.getProperty("secret-names") ?: return val secretNames = secretNamesProperty.split(",") val secrets = mutableMapOf() diff --git a/spring-boot-starter-waffle-secret-manager/src/main/resources/META-INF/spring.factories b/spring-boot-starter-waffle-secret-manager/src/main/resources/META-INF/spring.factories index 2799030..2835b73 100644 --- a/spring-boot-starter-waffle-secret-manager/src/main/resources/META-INF/spring.factories +++ b/spring-boot-starter-waffle-secret-manager/src/main/resources/META-INF/spring.factories @@ -1 +1 @@ -org.springframework.boot.env.EnvironmentPostProcessor=com.wafflestudio.spring.secretsmanager.config.SecretsManagerEnvironmentPostProcessor \ No newline at end of file +org.springframework.boot.EnvironmentPostProcessor=com.wafflestudio.spring.secretsmanager.config.SecretsManagerEnvironmentPostProcessor \ No newline at end of file diff --git a/truffle/truffle-core/src/main/kotlin/TruffleClient.kt b/truffle/truffle-core/src/main/kotlin/TruffleClient.kt index 8fd6536..e8db6d1 100644 --- a/truffle/truffle-core/src/main/kotlin/TruffleClient.kt +++ b/truffle/truffle-core/src/main/kotlin/TruffleClient.kt @@ -2,14 +2,16 @@ package com.wafflestudio.spring.truffle.core import com.wafflestudio.spring.truffle.core.protocol.TruffleEvent import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch +import org.apache.hc.client5.http.config.ConnectionConfig +import org.apache.hc.client5.http.impl.classic.HttpClients +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager +import org.apache.hc.core5.util.Timeout import org.slf4j.LoggerFactory import org.springframework.http.client.HttpComponentsClientHttpRequestFactory import org.springframework.web.client.RestClient -import java.time.Duration import java.util.concurrent.Executors internal interface TruffleClient { @@ -31,30 +33,44 @@ internal class DefaultTruffleClient( Thread(r, "truffle-client").apply { isDaemon = true } }.asCoroutineDispatcher(), ) - val clientHttpRequestFactory = - HttpComponentsClientHttpRequestFactory().apply { - setConnectTimeout(Duration.ofSeconds(5)) - setConnectionRequestTimeout(Duration.ofSeconds(1)) + + val connectionManager = + PoolingHttpClientConnectionManager().apply { + maxTotal = 3 + defaultMaxPerRoute = 3 + setDefaultConnectionConfig( + ConnectionConfig.custom() + .setSocketTimeout(Timeout.ofSeconds(5)) + .setConnectTimeout(Timeout.ofSeconds(5)) + .build(), + ) } + val httpClient = + HttpClients.custom() + .setConnectionManager(connectionManager) + .build() + + val requestFactory = HttpComponentsClientHttpRequestFactory(httpClient) + val restClient = restClientBuilder - .requestFactory(clientHttpRequestFactory) + .requestFactory(requestFactory) .baseUrl("https://truffle-api.wafflestudio.com") .defaultHeader("x-api-key", apiKey) .build() - coroutineScope.launch(SupervisorJob()) { - events.collect { + coroutineScope.launch { + events.collect { event -> runCatching { restClient .post() .uri("/events") - .body(it) + .body(event) .retrieve() .toBodilessEntity() - }.getOrElse { - logger.warn("Failed to request to truffle server", it) + }.getOrElse { error -> + logger.warn("Failed to request to truffle server", error) } } } diff --git a/truffle/truffle-core/src/main/kotlin/protocol/TruffleAppInfo.kt b/truffle/truffle-core/src/main/kotlin/protocol/TruffleAppInfo.kt deleted file mode 100644 index 4276f19..0000000 --- a/truffle/truffle-core/src/main/kotlin/protocol/TruffleAppInfo.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.wafflestudio.spring.truffle.core.protocol - -object TruffleAppInfo { - val runtime = TruffleRuntime() - - data class TruffleRuntime( - val name: String = "Java", - val version: String = System.getProperty("java.version"), - ) -} diff --git a/truffle/truffle-core/src/main/kotlin/protocol/TruffleEvent.kt b/truffle/truffle-core/src/main/kotlin/protocol/TruffleEvent.kt index 34b6daa..0f88f50 100644 --- a/truffle/truffle-core/src/main/kotlin/protocol/TruffleEvent.kt +++ b/truffle/truffle-core/src/main/kotlin/protocol/TruffleEvent.kt @@ -2,7 +2,16 @@ package com.wafflestudio.spring.truffle.core.protocol data class TruffleEvent( val version: String = TruffleVersion.V1, - val runtime: TruffleAppInfo.TruffleRuntime = TruffleAppInfo.runtime, + val runtime: TruffleRuntime = truffleRuntime, val level: TruffleLevel, val exception: TruffleException, +) { + companion object { + val truffleRuntime: TruffleRuntime = TruffleRuntime() + } +} + +data class TruffleRuntime( + val name: String = "Java", + val version: String = System.getProperty("java.version"), ) diff --git a/truffle/truffle-core/src/main/kotlin/protocol/TruffleException.kt b/truffle/truffle-core/src/main/kotlin/protocol/TruffleException.kt index f9fe695..f6abe1b 100644 --- a/truffle/truffle-core/src/main/kotlin/protocol/TruffleException.kt +++ b/truffle/truffle-core/src/main/kotlin/protocol/TruffleException.kt @@ -3,16 +3,16 @@ package com.wafflestudio.spring.truffle.core.protocol data class TruffleException( val className: String, val message: String?, - val elements: List, -) { - data class Element( - val className: String, - val methodName: String, - val lineNumber: Int, - val fileName: String, - val isInAppInclude: Boolean, - ) -} + val elements: List, +) + +data class TruffleExceptionElement( + val className: String, + val methodName: String, + val lineNumber: Int, + val fileName: String, + val isInAppInclude: Boolean, +) fun TruffleException(e: Throwable): TruffleException = TruffleException( @@ -20,7 +20,7 @@ fun TruffleException(e: Throwable): TruffleException = message = e.message, elements = e.stackTrace.map { - TruffleException.Element( + TruffleExceptionElement( className = it.className, methodName = it.methodName, lineNumber = it.lineNumber, diff --git a/truffle/truffle-core/src/main/resources/META-INF/native-image/reachability-metadata.json b/truffle/truffle-core/src/main/resources/META-INF/native-image/reachability-metadata.json new file mode 100644 index 0000000..c63765f --- /dev/null +++ b/truffle/truffle-core/src/main/resources/META-INF/native-image/reachability-metadata.json @@ -0,0 +1,52 @@ +{ + "reflection": [ + { + "type": "com.wafflestudio.spring.truffle.core.protocol.TruffleEvent", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredFields": true, + "allPublicFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true + }, + { + "type": "com.wafflestudio.spring.truffle.core.protocol.TruffleRuntime", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredFields": true, + "allPublicFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true + }, + { + "type": "com.wafflestudio.spring.truffle.core.protocol.TruffleException", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredFields": true, + "allPublicFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true + }, + { + "type": "com.wafflestudio.spring.truffle.core.protocol.TruffleExceptionElement", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredFields": true, + "allPublicFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "queryAllDeclaredMethods": true, + "queryAllDeclaredConstructors": true + }, + { + "type": "kotlin.Metadata", + "allDeclaredMethods": true + } + ] +} diff --git a/truffle/truffle-logback/src/main/kotlin/com/wafflestudio/spring/truffle/appender/TruffleAppender.kt b/truffle/truffle-logback/src/main/kotlin/com/wafflestudio/spring/truffle/appender/TruffleAppender.kt index 4f871ff..ece7beb 100644 --- a/truffle/truffle-logback/src/main/kotlin/com/wafflestudio/spring/truffle/appender/TruffleAppender.kt +++ b/truffle/truffle-logback/src/main/kotlin/com/wafflestudio/spring/truffle/appender/TruffleAppender.kt @@ -7,6 +7,7 @@ import com.wafflestudio.spring.truffle.core.IHub import com.wafflestudio.spring.truffle.core.Truffle import com.wafflestudio.spring.truffle.core.protocol.TruffleEvent import com.wafflestudio.spring.truffle.core.protocol.TruffleException +import com.wafflestudio.spring.truffle.core.protocol.TruffleExceptionElement import com.wafflestudio.spring.truffle.core.protocol.TruffleLevel class TruffleAppender : UnsynchronizedAppenderBase() { @@ -34,7 +35,7 @@ class TruffleAppender : UnsynchronizedAppenderBase() { message = eventObject.formattedMessage, elements = eventObject.callerData.map { - TruffleException.Element( + TruffleExceptionElement( className = it.className, methodName = it.methodName, fileName = it.fileName ?: "",