Skip to content

Commit f941d95

Browse files
committed
chore: java 23 and gradle 8.5 support
1 parent 2d8fdeb commit f941d95

File tree

20 files changed

+264
-156
lines changed

20 files changed

+264
-156
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ jre/
2525

2626
# Gradle release
2727
cx.json
28+
/.profileconfig.json

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ and [Compose Web (wasm)][Compose-Multiplatform] applications.
1818
```bash
1919
# Mac OS
2020
$ curl -s "https://get.sdkman.io" | bash
21-
$ sdk i java 22.ea-open
22-
$ sdk u java 22.ea-open
21+
$ sdk i java 23.ea-open
22+
$ sdk u java 23.ea-open
2323
```
2424

2525
### Build & Run
@@ -44,7 +44,7 @@ $ docker run \
4444
--publish 8081:8081 \
4545
--name kotlin-mpp-playground \
4646
--mount type=bind,source=$(pwd),destination=/app,readonly \
47-
openjdk:22-slim /bin/bash -c "printenv && nohup jwebserver -b 0.0.0.0 -p 8081 -d / & backend/jvm/build/libs/jvm-app"
47+
openjdk:23-slim /bin/bash -c "printenv && nohup jwebserver -b 0.0.0.0 -p 8081 -d / & backend/jvm/build/libs/jvm-app"
4848

4949
$ ./gradlew :backend:jvm:jibDockerBuild
5050
$ docker run -it --rm --name jvm-app -p 8080:8080 -p 9898:9898 sureshg/jvm
@@ -106,9 +106,9 @@ $ actionlint
106106

107107
<!-- Badges -->
108108

109-
[java_url]: https://jdk.java.net/22/
109+
[java_url]: https://jdk.java.net/23/
110110

111-
[java_img]: https://img.shields.io/badge/OpenJDK-22-e76f00?logo=openjdk&logoColor=e76f00
111+
[java_img]: https://img.shields.io/badge/OpenJDK-23-e76f00?logo=openjdk&logoColor=e76f00
112112

113113
[kt_url]: https://github.com/JetBrains/kotlin/releases/latest
114114

backend/jvm/build.gradle.kts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import com.google.devtools.ksp.gradle.KspTask
1+
import com.google.devtools.ksp.gradle.KspAATask
22
import common.*
33

44
plugins {
@@ -71,10 +71,13 @@ tasks {
7171
}
7272

7373
// Copy webapp to resources
74-
processResources { dependsOn(copyWebApp) }
74+
processResources {
75+
duplicatesStrategy = DuplicatesStrategy.INCLUDE
76+
dependsOn(copyWebApp)
77+
}
7578

7679
// Makes sure jte is generated before compilation
77-
withType<KspTask>().configureEach { dependsOn(generateJte) }
80+
withType<KspAATask>().configureEach { dependsOn(generateJte) }
7881

7982
// publish { finalizedBy(jibDockerBuild) }
8083
}
@@ -124,8 +127,8 @@ dependencies {
124127
implementation(libs.jte.runtime)
125128
// compileOnly(libs.jte.kotlin)
126129
implementation(libs.kotlinx.html) {
127-
version { strictly(libs.kotlinx.html.get().version.toString()) }
128-
because("Ktor Issue!")
130+
// version { strictly(libs.kotlinx.html.get().version.toString()) }
131+
// because("Ktor Issue!")
129132
}
130133
implementation(kotlinw("css"))
131134
implementation(libs.ktor.server.html)
@@ -140,7 +143,8 @@ dependencies {
140143
implementation(libs.ktor.cohort.core)
141144
implementation(libs.ktor.cohort.hikari)
142145
implementation(libs.micrometer.prometheus)
143-
implementation(libs.ap.loader.all)
146+
implementation(libs.ap.converter)
147+
// implementation(libs.ap.loader.all)
144148

145149
// Logging
146150
implementation(libs.logback.classic)

backend/jvm/src/main/java/one/converter/Arguments.java renamed to backend/jvm/src/main/java/Arguments.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
package one.converter;
21
/*
32
* Copyright 2022 Andrei Pangin
43
*
@@ -25,21 +24,23 @@
2524
public class Arguments {
2625
String title = "Flame Graph";
2726
String highlight;
27+
String state;
2828
Pattern include;
2929
Pattern exclude;
3030
double minwidth;
3131
int skip;
3232
boolean reverse;
33-
boolean cpu;
3433
boolean alloc;
3534
boolean live;
3635
boolean lock;
3736
boolean threads;
37+
boolean classify;
3838
boolean total;
3939
boolean lines;
4040
boolean bci;
4141
boolean simple;
4242
boolean dot;
43+
boolean norm;
4344
boolean collapsed;
4445
long from;
4546
long to;

backend/jvm/src/main/kotlin/dev/suresh/routes/Admin.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.ktor.server.routing.*
99
fun Route.adminRoutes() {
1010
swaggerUI(path = "swagger", swaggerFile = "openapi/documentation.yaml") {
1111
version = BuildConfig.swaggerUi
12+
customStyle("https://unpkg.com/[email protected]/themes/3.x/theme-flattop.css")
1213
}
1314

1415
get("/") { call.respondRedirect("/swagger") }

backend/jvm/src/main/kotlin/dev/suresh/routes/Mgmt.kt

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,39 @@
11
package dev.suresh.routes
22

3+
import Arguments
4+
import FlameGraph
35
import com.sun.management.HotSpotDiagnosticMXBean
46
import dev.suresh.jvmRuntimeInfo
57
import dev.suresh.plugins.debug
6-
import dev.suresh.runOnVirtualThread
78
import io.ktor.http.*
8-
import io.ktor.server.application.*
99
import io.ktor.server.http.content.*
10-
import io.ktor.server.request.*
1110
import io.ktor.server.response.*
1211
import io.ktor.server.routing.*
1312
import java.io.File
1413
import java.io.PrintStream
1514
import java.lang.management.ManagementFactory
1615
import jdk.jfr.Configuration
16+
import jdk.jfr.FlightRecorder
1717
import jdk.jfr.consumer.RecordingStream
18+
import jfr2flame
1819
import kotlin.io.path.*
1920
import kotlin.time.Duration.Companion.milliseconds
2021
import kotlin.time.Duration.Companion.minutes
22+
import kotlin.time.Duration.Companion.seconds
2123
import kotlin.time.toJavaDuration
2224
import kotlinx.coroutines.sync.Mutex
2325
import kotlinx.coroutines.sync.withLock
24-
import one.converter.Arguments
25-
import one.converter.FlameGraph
26-
import one.converter.jfr2flame
2726
import one.jfr.JfrReader
28-
import one.profiler.AsyncProfiler
29-
import one.profiler.AsyncProfilerLoader
30-
import one.profiler.Events
3127

3228
private val DEBUG = ScopedValue.newInstance<Boolean>()
3329

3430
val mutex = Mutex()
3531

36-
val profiler: AsyncProfiler? by lazy {
37-
val ap = AsyncProfilerLoader.loadOrNull()
38-
ap.start(Events.CPU, 1000)
39-
ap
40-
}
32+
// val profiler: AsyncProfiler? by lazy {
33+
// val ap = AsyncProfilerLoader.loadOrNull()
34+
// ap.start(Events.CPU, 1000)
35+
// ap
36+
// }
4137

4238
val docRoot = Path(System.getProperty("java.io.tmpdir"))
4339

@@ -158,44 +154,57 @@ fun Route.mgmtRoutes() {
158154
}
159155

160156
get("/profile") {
161-
// Run the blocking operation on virtual thread and make sure
162-
// only one profile operation is running at a time.
157+
// Make sure only one profile operation is running at a time.
163158
when {
164159
mutex.isLocked -> call.respondText("Profile operation is already running")
165160
else ->
166161
mutex.withLock {
167-
runOnVirtualThread {
168-
val jfrPath = createTempFile("profile", ".jfr")
169-
RecordingStream(Configuration.getConfiguration("profile")).use {
170-
it.setMaxSize(100 * 1024 * 1024)
171-
it.setMaxAge(2.minutes.toJavaDuration())
172-
it.enable("jdk.CPULoad").withPeriod(100.milliseconds.toJavaDuration())
173-
it.enable("jdk.JavaMonitorEnter").withStackTrace()
174-
it.startAsync()
175-
Thread.sleep(5_000)
176-
it.dump(jfrPath)
177-
println("JFR file written to ${jfrPath.toAbsolutePath()}")
162+
val jfrPath = createTempFile("profile", ".jfr")
163+
val flightRecorder = FlightRecorder.getFlightRecorder()
164+
when (flightRecorder.recordings.isEmpty()) {
165+
true -> {
166+
println("Starting new JFR recording...")
167+
RecordingStream(Configuration.getConfiguration("profile")).use {
168+
it.setMaxSize(100 * 1000 * 1000)
169+
it.setMaxAge(2.minutes.toJavaDuration())
170+
it.enable("jdk.CPULoad").withPeriod(100.milliseconds.toJavaDuration())
171+
it.enable("jdk.JavaMonitorEnter").withStackTrace()
172+
it.startAsync()
173+
Thread.sleep(5.seconds.inWholeMilliseconds)
174+
it.dump(jfrPath)
175+
}
178176
}
179-
180-
when (call.request.queryParameters.contains("download")) {
181-
true -> {
182-
call.response.header(
183-
HttpHeaders.ContentDisposition,
184-
ContentDisposition.Attachment.withParameter(
185-
ContentDisposition.Parameters.FileName, jfrPath.fileName.name)
186-
.toString())
187-
call.respondFile(jfrPath.toFile())
177+
else -> {
178+
println("Using existing JFR recording...")
179+
flightRecorder.takeSnapshot().use {
180+
if (it.size > 0) {
181+
it.maxSize = 50_000_000
182+
it.maxAge = 2.minutes.toJavaDuration()
183+
it.dump(jfrPath)
184+
}
188185
}
189-
else -> {
190-
val jfr2flame = jfr2flame(JfrReader(jfrPath.pathString), Arguments())
191-
val flameGraph = FlameGraph()
192-
jfr2flame.convert(flameGraph)
186+
}
187+
}
188+
println("JFR file written to ${jfrPath.toAbsolutePath()}")
189+
190+
when (call.request.queryParameters.contains("download")) {
191+
true -> {
192+
call.response.header(
193+
HttpHeaders.ContentDisposition,
194+
ContentDisposition.Attachment.withParameter(
195+
ContentDisposition.Parameters.FileName, jfrPath.fileName.name)
196+
.toString())
197+
call.respondFile(jfrPath.toFile())
198+
}
199+
else -> {
200+
val jfr2flame = jfr2flame(JfrReader(jfrPath.pathString), Arguments())
201+
val flameGraph = FlameGraph()
202+
jfr2flame.convert(flameGraph)
193203

194-
call.respondOutputStream(contentType = ContentType.Text.Html) {
195-
flameGraph.dump(PrintStream(this))
196-
}
197-
jfrPath.deleteIfExists()
204+
call.respondOutputStream(contentType = ContentType.Text.Html) {
205+
flameGraph.dump(PrintStream(this))
198206
}
207+
jfrPath.deleteIfExists()
199208
}
200209
}
201210
}

backend/jvm/src/main/resources/openapi/documentation.yaml

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,45 @@ paths:
1919
examples:
2020
Example#1:
2121
value: "/swagger"
22+
/browse/{param}:
23+
get:
24+
description: ""
25+
parameters:
26+
- name: "param"
27+
in: "path"
28+
required: true
29+
schema:
30+
type: "array"
31+
items:
32+
type: "string"
33+
responses:
34+
"200":
35+
description: "OK <br> A file response"
36+
content:
37+
application/*:
38+
schema:
39+
type: "object"
40+
format: "binary"
41+
"404":
42+
description: "Not Found"
43+
content:
44+
text/html:
45+
schema:
46+
type: "string"
47+
/ffm:
48+
get:
49+
description: ""
50+
/heapdump:
51+
get:
52+
description: ""
53+
responses:
54+
"200":
55+
description: "OK <br> A file response"
56+
content:
57+
application/*:
58+
schema:
59+
type: "object"
60+
format: "binary"
2261
/info:
2362
get:
2463
description: ""
@@ -28,4 +67,18 @@ paths:
2867
content:
2968
'*/*':
3069
schema:
31-
type: "string"
70+
type: "string"
71+
/profile:
72+
get:
73+
description: ""
74+
responses:
75+
"200":
76+
description: "OK <br> A file response"
77+
content:
78+
application/*:
79+
schema:
80+
type: "object"
81+
format: "binary"
82+
/vthreads:
83+
get:
84+
description: ""

common/src/jvmMain/resources/logback.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<configuration>
1+
<configuration scan="true" scanPeriod="30 seconds">
22

33
<property name="pattern"
44
value="%d{YYYY-MM-dd HH:mm:ss.SSS z, America/Los_Angeles} %-5level %X{remoteHost} [%thread] %logger{16} - %msg%n%rEx"/>

compose/desktop/build.gradle.kts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ dependencies {
1616
}
1717

1818
compose {
19-
// Workaround until compose has support for Kotlin 2.0
20-
kotlinCompilerPlugin = libs.versions.jetbrains.compose.compiler
21-
// kotlinCompilerPlugin = dependencies.compiler.forKotlin(kotlinVersion.get())
19+
kotlinCompilerPlugin = dependencies.compiler.forKotlin(kotlinVersion.get())
2220
kotlinCompilerPluginArgs.add("suppressKotlinVersionCompatibilityCheck=${kotlinVersion.get()}")
2321

2422
platformTypes = platformTypes.get() - KotlinPlatformType.native
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import androidx.compose.foundation.layout.*
2+
import androidx.compose.foundation.rememberScrollState
3+
import androidx.compose.foundation.verticalScroll
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.Modifier
6+
import androidx.compose.ui.draw.drawBehind
7+
import androidx.compose.ui.geometry.CornerRadius
8+
import androidx.compose.ui.graphics.Color
9+
import androidx.compose.ui.graphics.PathEffect
10+
import androidx.compose.ui.graphics.drawscope.Stroke
11+
import androidx.compose.ui.unit.dp
12+
13+
val stroke = Stroke(width = 2f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f))
14+
15+
fun Modifier.debug(color: Color = Color.Red) =
16+
then(
17+
if (debug)
18+
drawBehind {
19+
drawRoundRect(color = color, style = stroke, cornerRadius = CornerRadius(8.dp.toPx()))
20+
}
21+
else Modifier)
22+
23+
@Composable
24+
fun scrollingBox(modifier: Modifier = Modifier, content: @Composable () -> Unit) =
25+
BoxWithConstraints {
26+
val vscrollState = rememberScrollState()
27+
Box(
28+
modifier =
29+
modifier.fillMaxSize().padding(10.dp).debug().verticalScroll(state = vscrollState)) {
30+
content()
31+
}
32+
}

0 commit comments

Comments
 (0)