Skip to content

Commit f023d97

Browse files
committed
Init skeleton application for native compilation demonstration
1 parent e77fdab commit f023d97

28 files changed

+7634
-0
lines changed

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,14 @@
2222
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
2323
hs_err_pid*
2424
replay_pid*
25+
26+
/logs/
27+
/.idea/
28+
/build/
29+
/.gradle/
30+
/gradle/
31+
/native-app-db.db
32+
/gradlew
33+
/gradlew.bat
34+
35+
.lock

build.gradle.kts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
2+
import org.gradle.api.tasks.testing.logging.TestLogEvent
3+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
4+
5+
val jdkVersion = JavaVersion.VERSION_17
6+
val jacksonVersion = "2.14.0"
7+
val jacksonDatabindVersion = "2.14.0"
8+
val jacksonDataFormatVersion = "2.14.0"
9+
val vavrVersion = "0.10.2"
10+
val sqliteVersion = "3.43.0.0"
11+
val hibernateCommunityDialectsVersion = "6.1.6.Final"
12+
val mapstructVersion = "1.5.3.Final"
13+
val caffeineVersion = "3.1.6"
14+
val kotlinLoggingVersion = "4.0.0-beta-22"
15+
val kotestVersion = "5.6.2"
16+
val junitPioneerVersion = "2.0.1"
17+
val springmockkVersion = "4.0.2"
18+
val mockkVersion = "1.13.7"
19+
val springRetryVersion = "2.0.0"
20+
val jsonApiVersion = "1.10.1"
21+
val logstashLogbackEncoderVersion = "7.3"
22+
val okhttp3Version = "4.11.0"
23+
val awaitilityVersion = "4.2.0"
24+
val nativeImageConfigPath = "$projectDir/src/main/resources/META-INF/native-image"
25+
val nativeImageAccessFilterConfigPath = "./src/test/resources/native/access-filter.json"
26+
27+
plugins {
28+
val springBootVersion = "3.1.3"
29+
val kotlinVersion = "1.9.0"
30+
val testLoggerPluginVersion = "3.2.0"
31+
val springBootDependencyManagementPluginVersion = "1.1.0"
32+
val buildToolsNativeVersion = "0.9.21"
33+
34+
kotlin("jvm") version kotlinVersion
35+
kotlin("kapt") version kotlinVersion
36+
kotlin("plugin.spring") version kotlinVersion
37+
kotlin("plugin.jpa") version kotlinVersion
38+
id("org.jetbrains.kotlin.plugin.allopen") version kotlinVersion
39+
id("org.springframework.boot") version springBootVersion
40+
id("io.spring.dependency-management") version springBootDependencyManagementPluginVersion
41+
id("com.adarshr.test-logger").version(testLoggerPluginVersion)
42+
id("org.graalvm.buildtools.native") version buildToolsNativeVersion
43+
}
44+
45+
graalvmNative {
46+
binaries {
47+
named("main") {
48+
buildArgs(
49+
"-H:+ReportExceptionStackTraces",
50+
"-H:EnableURLProtocols=http,https",
51+
)
52+
}
53+
}
54+
}
55+
56+
group = "com.app"
57+
version = "1.0.0"
58+
java.sourceCompatibility = jdkVersion
59+
60+
repositories {
61+
mavenCentral()
62+
maven { url = uri("https://repo.spring.io/milestone") }
63+
mavenLocal()
64+
}
65+
66+
java {
67+
sourceCompatibility = jdkVersion
68+
targetCompatibility = jdkVersion
69+
70+
withJavadocJar()
71+
withSourcesJar()
72+
}
73+
74+
dependencies {
75+
implementation("org.springframework.boot:spring-boot-starter")
76+
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
77+
implementation("org.springframework.boot:spring-boot-starter-web")
78+
implementation("org.springframework.boot:spring-boot-starter-websocket")
79+
implementation("org.springframework.boot:spring-boot-starter-cache")
80+
implementation("org.jetbrains.kotlin:kotlin-reflect")
81+
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
82+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
83+
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion")
84+
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
85+
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion")
86+
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonDataFormatVersion")
87+
implementation("io.vavr:vavr-kotlin:$vavrVersion")
88+
implementation("org.flywaydb:flyway-core")
89+
implementation("org.xerial:sqlite-jdbc:$sqliteVersion")
90+
implementation("org.hibernate.orm:hibernate-community-dialects:$hibernateCommunityDialectsVersion")
91+
implementation("org.mapstruct:mapstruct:$mapstructVersion")
92+
implementation("com.github.ben-manes.caffeine:caffeine:$caffeineVersion")
93+
implementation("io.github.oshai:kotlin-logging-jvm:$kotlinLoggingVersion")
94+
implementation("org.springframework.retry:spring-retry:$springRetryVersion")
95+
implementation("net.logstash.logback:logstash-logback-encoder:$logstashLogbackEncoderVersion")
96+
implementation("com.squareup.okhttp3:okhttp:$okhttp3Version")
97+
implementation("com.slm-dev:jsonapi-simple:$jsonApiVersion")
98+
99+
kapt("org.mapstruct:mapstruct-processor:$mapstructVersion")
100+
101+
implementation("org.springframework.boot:spring-boot-starter-test")
102+
implementation("io.kotest:kotest-runner-junit5:$kotestVersion")
103+
implementation("io.kotest:kotest-assertions-core:$kotestVersion")
104+
implementation("io.kotest:kotest-property:$kotestVersion")
105+
implementation("io.kotest:kotest-assertions-json:$kotestVersion")
106+
implementation("org.junit-pioneer:junit-pioneer:$junitPioneerVersion")
107+
implementation("com.ninja-squad:springmockk:$springmockkVersion")
108+
testImplementation("org.awaitility:awaitility:$awaitilityVersion")
109+
testImplementation("io.mockk:mockk:$mockkVersion")
110+
}
111+
112+
allOpen {
113+
annotations("javax.persistence.Entity", "javax.persistence.MappedSuperclass", "javax.persistence.Embeddable")
114+
}
115+
116+
kapt {
117+
arguments {
118+
arg("mapstruct.defaultComponentModel", "spring")
119+
arg("mapstruct.unmappedTargetPolicy", "ERROR")
120+
}
121+
}
122+
123+
buildscript {
124+
repositories {
125+
maven {
126+
setUrl("https://plugins.gradle.org/m2/")
127+
}
128+
}
129+
}
130+
131+
tasks.withType<KotlinCompile> {
132+
kotlinOptions {
133+
freeCompilerArgs = listOf("-Xjsr305=strict")
134+
jvmTarget = jdkVersion.toString()
135+
}
136+
}
137+
138+
tasks.withType<Test> {
139+
useJUnitPlatform()
140+
141+
jvmArgs = listOf(
142+
"-agentlib:native-image-agent=access-filter-file=$nativeImageAccessFilterConfigPath,config-merge-dir=$nativeImageConfigPath"
143+
)
144+
testlogger {
145+
setTheme("mocha")
146+
}
147+
testLogging {
148+
events = setOf(
149+
TestLogEvent.STARTED,
150+
TestLogEvent.FAILED,
151+
TestLogEvent.PASSED,
152+
TestLogEvent.SKIPPED,
153+
TestLogEvent.STANDARD_ERROR,
154+
TestLogEvent.STANDARD_OUT
155+
)
156+
exceptionFormat = TestExceptionFormat.FULL
157+
showStandardStreams = false
158+
showCauses = true
159+
showExceptions = true
160+
showStackTraces = true
161+
}
162+
}
163+
164+
kapt {
165+
arguments {
166+
arg("mapstruct.defaultComponentModel", "spring")
167+
arg("mapstruct.unmappedTargetPolicy", "ERROR")
168+
}
169+
}
170+
171+
springBoot {
172+
buildInfo()
173+
}

ci/build.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import _thread
2+
import argparse as argparse
3+
import logging
4+
import os
5+
import signal
6+
import subprocess
7+
import time
8+
9+
logging.basicConfig(format='%(asctime)s [%(levelname)-5s] - %(message)s')
10+
11+
logger = logging.getLogger('builder')
12+
logger.setLevel("INFO")
13+
14+
15+
GRAALVM_HOME_PATH = "/Users/ev.mareycheva/opt/graalvm-jdk-17/Contents/Home"
16+
GRAALVM_JAVA_PATH = f"{GRAALVM_HOME_PATH}/bin/java"
17+
NATIVE_IMAGE_RESOURCES_PATH = "./src/main/resources/META-INF/native-image"
18+
19+
20+
def build_sources():
21+
subprocess.run(
22+
['gradle', 'clean', 'build', '-x', 'test'],
23+
env={**os.environ, 'JAVA_HOME': GRAALVM_HOME_PATH},
24+
)
25+
26+
27+
def start_aot_native_agent(aot_wait_timeout_minutes):
28+
process = subprocess.Popen(
29+
f'{GRAALVM_JAVA_PATH} "-Dspring.aot.enabled=true" -agentlib:native-image-agent=config-merge-dir={NATIVE_IMAGE_RESOURCES_PATH} -jar build/libs/kotlin-spring-boot-native-skeleton-1.0.0.jar',
30+
stdout=subprocess.PIPE,
31+
stderr=subprocess.STDOUT,
32+
shell=True
33+
)
34+
_thread.start_new_thread(print_aot_native_agent_logs, ('print-aot-logs', process.stdout))
35+
36+
time.sleep(aot_wait_timeout_minutes * 60)
37+
38+
os.kill(process.pid, signal.SIGTERM)
39+
40+
41+
def print_aot_native_agent_logs(_, stdout):
42+
with stdout:
43+
for line in iter(stdout.readline, b''):
44+
print(line.decode("utf-8").strip())
45+
46+
47+
def build_native_image():
48+
subprocess.run(['gradle', 'nativeCompile'])
49+
50+
51+
if __name__ == "__main__":
52+
args_parser = argparse.ArgumentParser()
53+
args_parser.add_argument(
54+
'--disable-build-native-image',
55+
default=False,
56+
action='store_true',
57+
help='Disable build native image'
58+
)
59+
args_parser.add_argument(
60+
'--aot-wait',
61+
default=20,
62+
type=int,
63+
help='Terminate AOT stage after N minutes'
64+
)
65+
args = args_parser.parse_args()
66+
67+
if args.disable_build_native_image:
68+
logger.info("Build native image step disabled. Run only build source and native-image-agent steps")
69+
70+
build_sources()
71+
start_aot_native_agent(args.aot_wait)
72+
73+
if not args.disable_build_native_image:
74+
build_native_image()

ci/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
argparse==1.4.0

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
kotlin.code.style=official

settings.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pluginManagement {
2+
repositories {
3+
maven { url = uri("https://repo.spring.io/milestone") }
4+
maven { url = uri("https://repo.spring.io/snapshot") }
5+
gradlePluginPortal()
6+
}
7+
}
8+
9+
rootProject.name = "kotlin-spring-boot-native-skeleton"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.app
2+
3+
import io.github.oshai.KotlinLogging
4+
import org.springframework.boot.autoconfigure.SpringBootApplication
5+
import org.springframework.boot.info.BuildProperties
6+
import org.springframework.boot.runApplication
7+
import java.time.LocalDate
8+
import java.time.ZoneOffset
9+
10+
@SpringBootApplication
11+
class MainApplication(
12+
private val buildProperties: BuildProperties,
13+
) {
14+
private val logger = KotlinLogging.logger {}
15+
16+
init {
17+
logger.info { "App version: ${buildProperties.version}" }
18+
logger.info { "App build time: ${LocalDate.ofInstant(buildProperties.time, ZoneOffset.UTC)}" }
19+
}
20+
}
21+
22+
fun main(args: Array<String>) {
23+
runApplication<MainApplication>(*args)
24+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.app.config
2+
3+
import org.springframework.boot.autoconfigure.domain.EntityScan
4+
import org.springframework.context.annotation.ComponentScan
5+
import org.springframework.context.annotation.Configuration
6+
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
7+
import org.springframework.retry.annotation.EnableRetry
8+
import org.springframework.scheduling.annotation.EnableScheduling
9+
import org.springframework.transaction.annotation.EnableTransactionManagement
10+
11+
@Configuration
12+
@EnableScheduling
13+
@EnableRetry
14+
@EnableTransactionManagement
15+
@EntityScan(basePackages = ["com.app.entity"])
16+
@EnableJpaRepositories(basePackages = ["com.app.repository"])
17+
@ComponentScan("com.app")
18+
class AppConfig
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.app.config
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude
4+
import com.fasterxml.jackson.databind.DeserializationFeature
5+
import com.fasterxml.jackson.databind.MapperFeature
6+
import com.fasterxml.jackson.databind.ObjectMapper
7+
import com.fasterxml.jackson.databind.SerializationFeature
8+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
9+
import com.fasterxml.jackson.module.kotlin.jacksonMapperBuilder
10+
import okhttp3.OkHttpClient
11+
import org.springframework.context.annotation.Bean
12+
import org.springframework.context.annotation.Configuration
13+
import org.springframework.retry.support.RetryTemplate
14+
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
15+
import org.springframework.web.client.RestTemplate
16+
import org.springframework.web.servlet.config.annotation.CorsRegistry
17+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
18+
19+
@Configuration
20+
class BeanConfig {
21+
companion object {
22+
fun newObjectMapper(includeNull: Boolean = false): ObjectMapper {
23+
val jacksonMapperBuilder = jacksonMapperBuilder()
24+
25+
jacksonMapperBuilder.addModule(JavaTimeModule())
26+
jacksonMapperBuilder.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
27+
jacksonMapperBuilder.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
28+
jacksonMapperBuilder.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
29+
30+
if (!includeNull) {
31+
jacksonMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL)
32+
}
33+
return jacksonMapperBuilder.build()
34+
}
35+
}
36+
37+
@Bean
38+
fun corsConfigurer(): WebMvcConfigurer {
39+
return object : WebMvcConfigurer {
40+
override fun addCorsMappings(registry: CorsRegistry) {
41+
registry.addMapping("/**")
42+
.allowedOrigins("*")
43+
.allowedMethods("*")
44+
}
45+
}
46+
}
47+
48+
@Bean
49+
fun taskScheduler(): ThreadPoolTaskScheduler {
50+
val threadPoolTaskScheduler = ThreadPoolTaskScheduler()
51+
threadPoolTaskScheduler.poolSize = 32
52+
53+
return threadPoolTaskScheduler
54+
}
55+
56+
@Bean
57+
fun objectMapper() = newObjectMapper()
58+
59+
@Bean
60+
fun retryTemplate(): RetryTemplate = RetryTemplate.builder()
61+
.maxAttempts(4)
62+
.fixedBackoff(500L)
63+
.retryOn(Exception::class.java)
64+
.build()
65+
66+
@Bean
67+
fun restTemplate() = RestTemplate()
68+
69+
@Bean
70+
fun okHttpClient() = OkHttpClient()
71+
}

0 commit comments

Comments
 (0)