Skip to content

Instantly share code, notes, and snippets.

@bgmf
Created January 21, 2021 15:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bgmf/05b8b4598bc02ccf83b226cc2313f579 to your computer and use it in GitHub Desktop.
Save bgmf/05b8b4598bc02ccf83b226cc2313f579 to your computer and use it in GitHub Desktop.
Failing GraalVM native-image build using Kotlin & JavaFX
package eu.dzim.kfxg
import eu.dzim.kfxg.fx.*
import eu.dzim.kfxg.res.resource
import eu.dzim.kfxg.utils.*
import javafx.application.Application
import javafx.event.EventHandler
import javafx.geometry.Insets
import javafx.geometry.Pos
import javafx.scene.Scene
import javafx.scene.control.Separator
import javafx.scene.layout.BorderPane
import javafx.scene.layout.Priority
import javafx.scene.layout.StackPane
import javafx.scene.layout.VBox
import javafx.scene.paint.Color
import javafx.scene.text.TextAlignment
import javafx.stage.Stage
import java.net.URI
import java.util.*
class App : Application() {
override fun start(primaryStage: Stage) {
logger.info("Application is starting...")
resource.also {
logger.fine("Resource: locale = ${resource.locale}")
logger.fine("Resource: resource-bundle = ${resource.resourceBundle}")
logger.fine("Resource: title = ${resource.getString("title")}")
logger.fine("Resource: title(!) = ${resource.getGuaranteedString("title")}")
logger.fine("Resource: title(b) = ${resource.getBinding("title")}")
}
logger.fine("java.library.path=${System.getProperty("java.library.path")}")
val task = SystemLanguageTask().apply {
setOnSucceeded {
(it.source.value as? Locale)?.also { l ->
logger.info("Switching locale to '$l'.")
resource.setLocale(l)
}
}
setOnFailed {
logger.severe("Couldn't detect the systems language.", it.source.exception)
}
}
ExecutorHelper.async()?.also {
logger.info("Submitting SystemLanguageTask...")
it.submit(task)
}
primaryStage.apply {
minWidth = 640.0
minHeight = 480.0
title = "Title"
titleProperty().bind(resource.getBinding("title"))
scene = StackPane().apply {
children += BorderPane().apply {
styleClass += "main-root"
// style = "-fx-background-color: transparent;"
center = VBox(10.0).apply {
alignment = Pos.TOP_CENTER
region { vgrow = Priority.ALWAYS }
label("Test App") {
margin = Insets(0.0, 0.0, 0.0, 50.0)
}
button("Download from 'openjdk.java.net'...") {
action { download() }
}
button("Print System Properties...") {
action { printSystemProperties(logger) }
}
hbox(10.0) {
region { hgrow = Priority.ALWAYS }
val b = button("Show current Locale...")
val l = label()
b.action { l.text = resource.locale?.toString() ?: "n/a" }
region { hgrow = Priority.ALWAYS }
}
button("Switch Locale...") {
action {
val def = resource.locale
if ("de" != def?.language) {
resource.setLanguage("de")
logger.fine("Resource: (DE) title(!) = ${resource.getGuaranteedString("title")}")
} else {
resource.setLanguage("en")
logger.fine("Resource: (EN) title(!) = ${resource.getGuaranteedString("title")}")
}
logger.fine("Switched locale to ${resource.locale}")
}
}
button("Show Dialog...") {
action {
OverlayDialog.showDialog<Boolean>(FXSize(max = 250), FXSize(max = 150)) {
VBox(10.0).apply {
hbox {
alignment = Pos.TOP_CENTER
label {
hgrow = Priority.ALWAYS
textAlignment = TextAlignment.CENTER
text = "dialog.title".translate()
}
}
region { vgrow = Priority.ALWAYS }
label {
maxWidth = Double.MAX_VALUE
alignment = Pos.CENTER
textAlignment = TextAlignment.CENTER
text = "dialog.content".translate()
}
region { vgrow = Priority.ALWAYS }
children += Separator()
hbox(5.0) {
region { hgrow = Priority.ALWAYS }
button {
text = "dialog.ok".translate()
action { it.close { true } }
}
button {
text = "dialog.cancel".translate()
action { it.close { false } }
}
}
}
}?.also {
logger.info("Dialog closed with button '${if (it) "dialog.ok".translate() else "dialog.cancel".translate()}'")
} ?: logger.warning("Dialog closed with no result.")
}
}
region { vgrow = Priority.ALWAYS }
}
}
}.also {
OverlayDialog.initDialog(it)
}.let {
Scene(it, minWidth, minHeight)
}.apply {
fill = Color.TRANSPARENT
}
onCloseRequest = EventHandler {
logger.info("Application is closing...")
ExecutorHelper.shutdown()
}
}.show()
}
private fun download() {
downloadString(URI.create("https://openjdk.java.net/"))?.also {
logger.fine(it)
}
}
companion object {
val logger by lazy { createLogger() }
}
}
package eu.dzim.kfxg.utils
import java.util.logging.Level
import java.util.logging.LogRecord
import java.util.logging.SimpleFormatter
import java.util.logging.StreamHandler
class ConsoleHandler : StreamHandler() {
init {
level = Level.ALL
formatter = SimpleFormatter()
setOutputStream(System.out)
}
override fun publish(record: LogRecord?) {
super.publish(record)
flush()
}
override fun close() {
flush()
}
}
package eu.dzim.kfxg.utils
import javafx.application.Platform
import javafx.util.Pair
import java.lang.Thread.UncaughtExceptionHandler
import java.util.*
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicLong
import java.util.function.Consumer
import java.util.function.Supplier
import java.util.logging.Logger
fun execAsync(): ExecutorService? = ExecutorHelper.async()
fun execAsync(ifNull: () -> Unit = {}, op: ExecutorService.() -> Unit) {
execAsync()?.also(op) ?: run(ifNull)
}
fun execOnAsync(ifNull: () -> Unit = {}, op: () -> Unit) {
execAsync(ifNull) { submit(op) }
}
fun execOnAsync(ifNull: () -> Unit = {}, op: Runnable) {
execAsync(ifNull) { submit(op) }
}
fun execFx(): Executor? = ExecutorHelper.fx()
fun execFx(ifNull: () -> Unit = {}, op: Executor.() -> Unit) {
execFx()?.also(op) ?: run(ifNull)
}
fun execOnFx(ifNull: () -> Unit = {}, op: () -> Unit) {
execFx(ifNull) { execute(op) }
}
fun <T> doAsynchronously(
prepareOnUI: () -> Unit = {},
failOnUI: Exception.() -> Unit = {},
finishOnUI: T?.() -> Unit = {},
doAsync: () -> T?,
) {
ExecutorHelper.doAsynchronously(
prepareOnUI,
doAsync,
{ e -> e?.failOnUI() },
{ t -> t.finishOnUI() },
)
}
object ExecutorHelper {
private val LOGGER = Logger.getLogger(ExecutorHelper::class.java.name)
private var fx: Executor? = null
private var async: ExecutorService? = null
private var shutdown = false
// var fx: Executor? = null
// get() {
// if (shutdown) {
// LOGGER.warning("ExecutorHelper is already in shutdown. Can't accept new requests.")
// return null
// }
// if (field == null) field = Executor { treatment: Runnable? -> PlatformHelper.run(treatment) }
// return field
// }
// private set
// var async: ExecutorService? = null
// get() {
// if (shutdown) {
// LOGGER.warning("ExecutorHelper is already in shutdown. Can't accept new requests.")
// return null
// }
// if (field == null) field = initExecutorService("task-%d")
// return field
// }
// private set
@Suppress("MoveSuspiciousCallableReferenceIntoParentheses")
fun fx(): Executor? {
if (shutdown) {
LOGGER.warning("ExecutorHelper is already in shutdown. Can't accept new requests.")
return null
}
if (fx == null) fx = Executor { runOnFXThread { it.run() } }
return fx
}
fun async(): ExecutorService? {
if (shutdown) {
LOGGER.warning("ExecutorHelper is already in shutdown. Can't accept new requests.")
return null
}
if (async == null) async = initExecutorService("task-%d")
return async
}
fun <T> doAsynchronously(
prepareOnUI: Runnable?,
doAsync: Supplier<T?>?,
failOnUI: Consumer<Exception?>?,
finishOnUI: Consumer<T?>?,
) {
if (shutdown) {
LOGGER.warning("ExecutorHelper is already in shutdown. Can't accept new requests.")
return
}
val fx = fx()
val async = async()
if (fx == null || async == null) {
LOGGER.warning("Couldn't initiate necessary Executors. Therefore I couldn't execute the request.")
return
}
CompletableFuture
.supplyAsync<Any?>({
try {
prepareOnUI?.run()
} catch (e: Exception) {
LOGGER.severe("Exception caught during on-UI-preparation: " + e.message, e)
}
null
}, fx)
.thenApplyAsync({
val result: Pair<T?, Exception?>
if (doAsync == null) {
result = Pair(null, null)
return@thenApplyAsync result
}
result = try {
Pair(doAsync.get(), null)
} catch (e: Exception) {
LOGGER.severe("Exception caught during async-task: " + e.message, e)
Pair(null, e)
}
result
}, async)
.thenAcceptAsync({ pair: Pair<T?, Exception?> ->
if (pair.value != null && failOnUI != null) {
try {
failOnUI.accept(pair.value)
} catch (e: Exception) {
LOGGER.severe("Exception caught during on-UI-failure handling: " + e.message, e)
}
}
if (finishOnUI != null) {
try {
finishOnUI.accept(pair.key)
} catch (e: Exception) {
LOGGER.severe("Exception caught during on-UI-finish handling: " + e.message, e)
}
}
}, fx)
}
fun shutdown() {
shutdown = true
if (fx != null) {
fx = null
LOGGER.fine("FX Executor is dereferenced.")
}
if (async != null) shutdownExecutorService(async)
}
fun runOnFXThread(op: () -> Unit) {
if (Platform.isFxApplicationThread()) {
op()
} else {
Platform.runLater(op)
}
}
private fun initExecutorService(nameFormat: String): ExecutorService {
return Executors.newSingleThreadExecutor(
createThreadFactory(
nameFormat,
null,
null,
) { t: Thread, e: Throwable ->
LOGGER.severe(
String.format(
"Uncaught exception on thread '%s' (id=%s, prio=%d). State was %s. Message was: %s",
t.name,
t.id,
t.priority,
t.state,
e.message,
),
e,
)
})
}
private fun createThreadFactory(
nameFormat: String?,
daemon: Boolean?,
priority: Int?,
uncaughtExceptionHandler: UncaughtExceptionHandler?,
): ThreadFactory {
val backingThreadFactory = Executors.defaultThreadFactory()
val count = if (nameFormat != null) AtomicLong(0L) else null
return ThreadFactory { runnable: Runnable? ->
val thread = backingThreadFactory.newThread(runnable)
if (nameFormat != null) thread.name = String.format(Locale.ROOT, nameFormat, count!!.getAndIncrement())
if (daemon != null) thread.isDaemon = daemon
if (priority != null) thread.priority = priority
if (uncaughtExceptionHandler != null) thread.uncaughtExceptionHandler = uncaughtExceptionHandler
thread
}
}
private fun shutdownExecutorService(executorService: ExecutorService?) {
executorService!!.shutdown()
try {
executorService.awaitTermination(1, TimeUnit.SECONDS)
} catch (e: InterruptedException) {
LOGGER.severe("Problem while waiting for async scheduler termination.", e)
} finally {
executorService.shutdownNow()
LOGGER.fine((if (executorService === async) "Async" else "Unknown") + " Executor is shutting down now.")
}
}
}
package eu.dzim.kfxg.fx
import javafx.beans.value.ObservableValue
import javafx.event.EventTarget
import javafx.geometry.Insets
import javafx.geometry.Pos
import javafx.scene.Group
import javafx.scene.Node
import javafx.scene.Parent
import javafx.scene.control.*
import javafx.scene.layout.*
import java.lang.reflect.Method
/**
* Helper data class containing the `min`, `pref` and `max` value for either width or height of a JavaFX node.
*/
data class FXSize(
val min: Number? = null,
val pref: Number? = null,
val max: Number? = null,
)
fun Region.applySize(width: FXSize? = null, height: FXSize? = null) {
width?.also {
it.min?.also { v -> minWidth = v.toDouble() } ?: run { minWidth = Region.USE_PREF_SIZE }
it.pref?.also { v -> prefWidth = v.toDouble() }
it.max?.also { v -> maxWidth = v.toDouble() } ?: run { maxWidth = Double.MAX_VALUE }
} ?: run {
minWidth = Region.USE_PREF_SIZE
maxWidth = Double.MAX_VALUE
}
height?.also {
it.min?.also { v -> minHeight = v.toDouble() } ?: run { minHeight = Region.USE_PREF_SIZE }
it.pref?.also { v -> prefHeight = v.toDouble() }
it.max?.also { v -> maxHeight = v.toDouble() } ?: run { maxHeight = Double.MAX_VALUE }
} ?: run {
minHeight = Region.USE_PREF_SIZE
maxHeight = Double.MAX_VALUE
}
}
/*
* DISCLAIMER: this is an stripped down extract from TornadoFX (for example Layouts.kt)
*/
fun EventTarget.borderPane(op: BorderPane.() -> Unit = {}): BorderPane = opcr(this, BorderPane(), op)
fun EventTarget.hbox(spacing: Number? = null, alignment: Pos? = null, op: HBox.() -> Unit = {}): HBox {
val hbox = HBox()
if (alignment != null) hbox.alignment = alignment
if (spacing != null) hbox.spacing = spacing.toDouble()
return opcr(this, hbox, op)
}
fun EventTarget.vbox(spacing: Number? = null, alignment: Pos? = null, op: VBox.() -> Unit = {}): VBox {
val vbox = VBox()
if (alignment != null) vbox.alignment = alignment
if (spacing != null) vbox.spacing = spacing.toDouble()
return opcr(this, vbox, op)
}
var Node.hgrow: Priority?
get() = HBox.getHgrow(this)
set(value) {
HBox.setHgrow(this, value)
}
var Node.vgrow: Priority?
get() = VBox.getVgrow(this)
set(value) {
VBox.setVgrow(this, value)
}
var Node.borderPaneAlignment: Pos?
get() = BorderPane.getAlignment(this)
set(value) {
BorderPane.setAlignment(this, value)
}
var Node.margin: Insets?
get() = when (this.parent) {
is VBox -> VBox.getMargin(this)
is HBox -> HBox.getMargin(this)
is BorderPane -> BorderPane.getMargin(this)
else -> null
}
set(value) {
when (this.parent) {
is VBox -> VBox.setMargin(this, value)
is HBox -> HBox.setMargin(this, value)
is BorderPane -> BorderPane.setMargin(this, value)
else -> null
}
}
fun EventTarget.region(op: Region.() -> Unit = {}) = opcr(this, Region(), op)
fun EventTarget.label(text: String = "", graphic: Node? = null, op: Label.() -> Unit = {}) = Label(text).attachTo(this, op) {
if (graphic != null) it.graphic = graphic
}
fun EventTarget.button(text: String = "", graphic: Node? = null, op: Button.() -> Unit = {}) = Button(text).attachTo(this, op) {
if (graphic != null) it.graphic = graphic
}
fun EventTarget.button(text: ObservableValue<String>, graphic: Node? = null, op: Button.() -> Unit = {}) = Button().attachTo(this, op) {
it.textProperty().bind(text)
if (graphic != null) it.graphic = graphic
}
fun ButtonBase.action(op: () -> Unit) = setOnAction { op() }
/**
* Add the given node to the pane, invoke the node operation and return the node. The `opcr` name
* is an acronym for "op connect & return".
*/
inline fun <T : Node> opcr(parent: EventTarget, node: T, op: T.() -> Unit = {}) = node.apply {
parent.addChildIfPossible(this)
op(this)
}
/**
* Attaches the node to the pane and invokes the node operation.
*/
inline fun <T : Node> T.attachTo(parent: EventTarget, op: T.() -> Unit = {}): T = opcr(parent, this, op)
/**
* Attaches the node to the pane and invokes the node operation.
* Because the framework sometimes needs to setup the node, another lambda can be provided
*/
internal inline fun <T : Node> T.attachTo(
parent: EventTarget,
after: T.() -> Unit,
before: (T) -> Unit
) = this.also(before).attachTo(parent, after)
/**
* Find the list of children from a Parent node. Gleaned code from ControlsFX for this.
*/
fun EventTarget.getChildList(): MutableList<Node>? = when (this) {
is SplitPane -> items
is ToolBar -> items
is Pane -> children
is Group -> children
is HBox -> children
is VBox -> children
is Control -> (skin as? SkinBase<*>)?.children ?: getChildrenReflectively()
is Parent -> getChildrenReflectively()
else -> null
}
@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
private fun Parent.getChildrenReflectively(): MutableList<Node>? {
val getter = this.javaClass.findMethodByName("getChildren")
if (getter != null && java.util.List::class.java.isAssignableFrom(getter.returnType)) {
getter.isAccessible = true
return getter.invoke(this) as MutableList<Node>
}
return null
}
fun Class<*>.findMethodByName(name: String): Method? {
val method = (declaredMethods + methods).find { it.name == name }
if (method != null) return method
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
if (superclass == java.lang.Object::class.java) return null
return superclass.findMethodByName(name)
}
@Suppress("UNNECESSARY_SAFE_CALL")
fun EventTarget.addChildIfPossible(node: Node, index: Int? = null) {
if (this is Node) {
// val target = builderTarget
// if (target != null) {
// // Trick to get around the disallowed use of invoke on out projected types
// @Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
// target!!(this).value = node
// return
// }
}
when (this) {
is ScrollPane -> content = node
is Tab -> {
// Map the tab to the UIComponent for later retrieval. Used to close tab with UIComponent.close()
// and to connect the onTabSelected callback
content = node
}
is ButtonBase -> {
graphic = node
}
is BorderPane -> {
} // Either pos = builder { or caught by builderTarget above
is TabPane -> {
val tab = Tab(node.toString(), node)
tabs.add(tab)
}
is TitledPane -> {
when (content) {
is Pane -> content.addChildIfPossible(node, index)
is Node -> {
val container = VBox()
container.children.addAll(content, node)
content = container
}
else -> content = node
}
}
is CustomMenuItem -> {
content = node
}
is MenuItem -> {
graphic = node
}
else -> getChildList()?.apply {
if (!contains(node)) {
if (index != null && index < size)
add(index, node)
else
add(node)
}
}
}
}
# src/main/resources/res
handlers=eu.dzim.kfxg.utils.ConsoleHandler
.level=INFO
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$tdT%1$tH:%1$tM:%1$tS.%1$tL;%4$s;%3$s;%2$s;%5$s%6$s%n
java.util.logging.FileHandler.pattern=./output%g.log
java.util.logging.FileHandler.limit=50000000
java.util.logging.FileHandler.count=10
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
eu.dzim.kfxg.utils.ConsoleHandler.level=ALL
eu.dzim.kfxg.utils.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
eu.dzim.kfxg.level=ALL
package eu.dzim.kfxg
import javafx.application.Application
fun main(args: Array<String>) {
Application.launch(App::class.java, *args)
}
package eu.dzim.kfxg.fx
import eu.dzim.kfxg.utils.createLogger
import eu.dzim.kfxg.utils.severe
import javafx.application.Platform
import javafx.geometry.Pos
import javafx.scene.effect.BlurType
import javafx.scene.effect.DropShadow
import javafx.scene.layout.Background
import javafx.scene.layout.BackgroundFill
import javafx.scene.layout.Pane
import javafx.scene.layout.StackPane
import javafx.scene.paint.Color
class OverlayDialog(
private val container: StackPane,
) {
private var _result: Any? = null
private var initialized = false
private val lock = Any()
private var overlay = StackPane()
fun <T> show(
width: FXSize? = null,
height: FXSize? = null,
contentSupplier: () -> Pane,
): T? {
init()
val content = contentSupplier().apply {
applySize(width, height)
setStyle("-fx-background-color: white; -fx-padding: 5.0; -fx-background-radius: 5.0; -fx-border-color: black; -fx-border-radius: 5.0;")
effect = DropShadow(BlurType.GAUSSIAN, Color.web("#000000"), 25.0, 0.1,0.0, 0.0)
}
StackPane.setAlignment(content, Pos.CENTER)
overlay.apply {
children += content
opacity = 1.0
toFront()
}
// This forces the dialog to be modal
try {
@Suppress("UNCHECKED_CAST")
_result = Platform.enterNestedEventLoop(lock) as T?
} catch (e: Exception) {
logger.severe(e.message ?: "", e)
}
return getResult()
}
fun <T> close(resultSupplier: (() -> T?)? = null) {
resultSupplier?.also { _result = it() }
overlay.apply {
children.clear()
opacity = 0.0
toBack()
}
try {
@Suppress("UNCHECKED_CAST")
Platform.exitNestedEventLoop(lock, _result)
} catch (e: Exception) {
logger.severe(e.message ?: "", e)
}
initialized = false
}
@Suppress("UNCHECKED_CAST")
fun <T> getResult(): T? = _result as? T?
fun <T> setResult(result: T?) {
_result = result
}
private fun init() {
if (initialized) return
StackPane.setAlignment(overlay, Pos.CENTER)
overlay = container.lookup(".overlay") as? StackPane ?: StackPane().apply {
styleClass += "overlay"
alignment = Pos.CENTER
background = Background(BackgroundFill(Color.rgb(0, 0, 0, 0.7), null, null))
opacity = 0.0
}
if (!container.children.contains(overlay)) {
container.children += overlay
}
overlay.toBack()
initialized = true
}
companion object {
val logger by lazy { createLogger() }
private var container: StackPane? = null
private var dialog: OverlayDialog? = null
fun initDialog(container: StackPane) {
Companion.container = container
dialog = OverlayDialog(container)
}
fun <T> showDialog(
width: FXSize? = null,
height: FXSize? = null,
contentSupplier: (OverlayDialog) -> Pane,
): T? =
dialog?.let { it.show<T>(width, height) { contentSupplier(it) } }
val isDialogShown: Boolean get() = dialog?.overlay?.children?.isNotEmpty() == true
fun closeOpenDialog() {
if (isDialogShown) dialog?.close<Any>()
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>eu.dzim</groupId>
<artifactId>kotlin-javafx-graal</artifactId>
<version>0.0.0</version>
<packaging>jar</packaging>
<name>KotlinFX-Graal</name>
<description>Simple app to showcase GraalVM native-image issue.</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<kotlin.version>1.4.21</kotlin.version>
<kotlin.code.style>official</kotlin.code.style>
<junit.version>4.12</junit.version>
<javafx.version>15</javafx.version>
<javafx.plugin.version>0.0.5</javafx.plugin.version>
<client.plugin.version>0.1.35</client.plugin.version>
<mainClassName>eu.dzim.kfxg.MainKt</mainClassName>
</properties>
<repositories>
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>gluon-releases</id>
<url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.plugin.version}</version>
<configuration>
<mainClass>${mainClassName}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>client-maven-plugin</artifactId>
<version>${client.plugin.version}</version>
<configuration>
<!-- <target>ios|android|host</target> -->
<mainClass>${mainClassName}</mainClass>
<!-- basically ignored, I could not read and properties using ResourceBundle -->
<bundlesList>
<list>res.strings</list>
<list>res.strings_de</list>
<list>res.strings_en</list>
</bundlesList>
<!-- Workaround: write some custom logic, to circumvent the ResourceBundle issue - easy -->
<resourcesList>
<list>logging.properties</list>
<list>res/strings.properties</list>
<list>res/strings_de.properties</list>
<list>res/strings_en.properties</list>
</resourcesList>
<reflectionList>
<!-- UI, when you don't use reflection, the main classes are enough -->
<list>eu.dzim.kfxg.MainKt</list>
<list>eu.dzim.kfxg.App</list>
<!-- other, e.g. logging -->
<list>eu.dzim.kfxg.utils.ConsoleHandler</list>
<!-- our own jackson data classes -->
<!-- Jackson -->
<list>com.fasterxml.jackson.core.JsonFactory</list>
</reflectionList>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>11</jvmTarget>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
# src/main/resources/META-INF/substrate/config - remove this line!
[
{
"name": "com.fasterxml.jackson.databind.ObjectMapper",
"methods": [
{ "name": "<init>", "parameterTypes": [] },
{ "name": "<init>", "parameterTypes": ["com.fasterxml.jackson.core.JsonFactory"] }
]
}
]
package eu.dzim.kfxg.res
import eu.dzim.kfxg.utils.createLogger
import eu.dzim.kfxg.utils.severe
import javafx.beans.binding.*
import javafx.beans.property.ReadOnlyObjectProperty
import javafx.beans.property.ReadOnlyObjectWrapper
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.util.*
val resource: Resource
get() = SimpleResourceImpl
/**
* - always to path structure for package, like '/res'
* - resource property file names as usual
* - only language will be used from the locales, so no 'strings_de_DE.properties', only 'string_de.properties'
*/
object SimpleResourceImpl : SimpleBaseResource("res", "strings", Locale.ENGLISH) {
var instance: SimpleResourceImpl? = null
get() {
if (field == null) field = this
return field
}
private set
}
interface Resource {
fun setLanguage(language: String): Boolean
fun setLocale(locale: Locale): Boolean
fun localeProperty(): ReadOnlyObjectProperty<Locale?>
val locale: Locale?
fun resourceBundleProperty(): ReadOnlyObjectProperty<ResourceBundle?>
val resourceBundle: ResourceBundle?
fun getGuaranteedString(key: String): String
fun getGuaranteedString(locale: Locale, key: String): String
fun getGuaranteedString(key: String, vararg args: Any): String
fun getGuaranteedString(locale: Locale, key: String, vararg args: Any): String
fun getString(key: String): String?
fun getString(key: String, vararg args: Any): String?
fun getBoolean(key: String): Boolean?
fun getBoolean(key: String, defaultValue: Boolean?): Boolean?
fun getInteger(key: String): Int?
fun getInteger(key: String, defaultValue: Int?): Int?
fun getLong(key: String): Long?
fun getLong(key: String, defaultValue: Long?): Long?
fun getDouble(key: String): Double?
fun getDouble(key: String, defaultValue: Double?): Double?
fun getBinding(key: String, vararg parameter: Any): Binding<String?>
fun <T> getBinding(key: String, defaultValue: T?, vararg parameter: Any): Binding<T?>
fun setParentResource(parentResource: Resource)
}
interface Disposable {
fun dispose()
}
class DisposableHolder {
private val disposables: List<Disposable> = Collections.synchronizedList(ArrayList())
fun disposeAll() {
disposables.stream().forEach { it.dispose() }
}
}
abstract class SimpleBaseResource protected constructor(
private val resourcePath: String = DEFAULT_PACKAGE_NAME,
private val resourceFilePrefix: String = DEFAULT_PROPERTIES_NAME,
private val defaultLocale: Locale? = Locale.ENGLISH
) : Resource, Disposable {
private var parentResource: Resource?
private val bundleName: String
private val localeRO = ReadOnlyObjectWrapper(this, "locale", Locale.getDefault())
private val localizedResources: MutableMap<Locale, Properties?> = HashMap()
private val resourcesRO = ReadOnlyObjectWrapper(this, "resources", null as Properties?)
private val keyStringBindings: MutableMap<String, StringBinding?> = HashMap()
private val keyBooleanBindings: MutableMap<String, BooleanBinding?> = HashMap()
private val keyIntegerBindings: MutableMap<String, IntegerBinding?> = HashMap()
private val keyLongBindings: MutableMap<String, LongBinding?> = HashMap()
private val keyDoubleBindings: MutableMap<String, DoubleBinding?> = HashMap()
@Synchronized
override fun setLanguage(language: String): Boolean {
val next = Locale(language.toLowerCase())
return setLocale(next)
}
override fun setParentResource(parentResource: Resource) {
this.parentResource = parentResource
}
@Synchronized
override fun setLocale(locale: Locale): Boolean {
val current = this.locale
if (current == null || current != locale) {
if (localizedResources[locale] != null) {
resourcesRO.set(localizedResources[locale])
} else {
resourcesRO.set(loadProperties(resourcePath, resourceFilePrefix, locale))
localizedResources[locale] = resourcesRO.get()
}
localeRO.set(locale)
}
return false
}
@Synchronized
override fun localeProperty(): ReadOnlyObjectProperty<Locale?> = localeRO.readOnlyProperty
override val locale: Locale?
get() = localeRO.get()
@Synchronized
override fun resourceBundleProperty(): ReadOnlyObjectProperty<ResourceBundle?> =
ReadOnlyObjectWrapper(this, "resourceBundle", null as ResourceBundle?).readOnlyProperty
override val resourceBundle: ResourceBundle?
get() = null
@Synchronized
override fun getGuaranteedString(key: String): String =
try {
getPropertyWithFallback(key)?.trim { it <= ' ' }
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getGuaranteedString(key)?.trim { it <= ' ' } ?: "!$key!"
}
override fun getGuaranteedString(locale: Locale, key: String): String {
var rb = localizedResources[locale]
if (rb == null) {
rb = loadProperties(resourcePath, resourceFilePrefix, locale)
localizedResources[locale] = rb
}
return if (localizedResources[locale] != null) {
try {
rb?.getProperty(key)?.trim { it <= ' ' }
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getGuaranteedString(locale, key)?.trim { it <= ' ' } ?: "!$key!"
}
} else {
getGuaranteedString(key)
}
}
@Synchronized
override fun getGuaranteedString(key: String, vararg args: Any): String {
val value = try {
getPropertyWithFallback(key)?.trim { it <= ' ' }
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getGuaranteedString(key, *args)?.trim { it <= ' ' } ?: "!$key!"
}
return if (value.startsWith("!") && value.endsWith("!"))
value
else
String.format(locale, value, *args)
}
override fun getGuaranteedString(locale: Locale, key: String, vararg args: Any): String {
var rb = localizedResources[locale]
if (rb == null) {
rb = loadProperties(resourcePath, resourceFilePrefix, locale)
localizedResources[locale] = rb
}
return if (localizedResources[locale] != null) {
val value = try {
rb?.getProperty(key)?.trim { it <= ' ' }
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getGuaranteedString(locale, key, *args)?.trim { it <= ' ' } ?: "!$key!"
}
if (value.startsWith("!") && value.endsWith("!")) value
else String.format(locale, value, *args)
} else {
getGuaranteedString(key, *args)
}
}
@Synchronized
override fun getString(key: String): String? =
try {
getPropertyWithFallback(key)?.trim { it <= ' ' }
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getString(key)
}
@Synchronized
override fun getString(key: String, vararg args: Any): String? {
val value: String = try {
getPropertyWithFallback(key)?.trim { it <= ' ' }
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getString(key, *args) ?: "!$key!"
}
return if (value.startsWith("!") && value.endsWith("!"))
null
else
String.format(locale, value, *args)
}
@Synchronized
override fun getBoolean(key: String): Boolean? =
try {
getPropertyWithFallback(key)?.trim { it <= ' ' }?.toBoolean()
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getBoolean(key)
}
@Synchronized
override fun getBoolean(key: String, defaultValue: Boolean?): Boolean? =
getBoolean(key) ?: defaultValue
@Synchronized
override fun getInteger(key: String): Int? =
try {
getPropertyWithFallback(key)?.trim { it <= ' ' }?.toInt()
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getInteger(key)
} catch (e: NumberFormatException) {
null
}
@Synchronized
override fun getInteger(key: String, defaultValue: Int?): Int? =
getInteger(key) ?: defaultValue
@Synchronized
override fun getLong(key: String): Long? =
try {
getPropertyWithFallback(key)?.trim { it <= ' ' }?.toLong()
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getLong(key)
} catch (e: NumberFormatException) {
null
}
@Synchronized
override fun getLong(key: String, defaultValue: Long?): Long? =
getLong(key) ?: defaultValue
@Synchronized
override fun getDouble(key: String): Double? =
try {
getPropertyWithFallback(key)?.trim { it <= ' ' }?.toDouble()
?: throw MissingResourceException("key '$key' not found", this::class.simpleName, key)
} catch (e: MissingResourceException) {
parentResource?.getDouble(key)
} catch (e: NumberFormatException) {
null
}
@Synchronized
override fun getDouble(key: String, defaultValue: Double?): Double? =
getDouble(key) ?: defaultValue
override fun getBinding(key: String, vararg parameter: Any): Binding<String?> =
createStringBinding(key, null, *parameter)!!
@Suppress("UNCHECKED_CAST")
override fun <T> getBinding(key: String, defaultValue: T?, vararg parameter: Any): Binding<T?> {
return when (defaultValue) {
null -> getBinding(key, *parameter) as Binding<T?>
is String -> createStringBinding(key, defaultValue as String, *parameter) as Binding<T?>
is Boolean -> createBooleanBinding(key, defaultValue as Boolean) as Binding<T?>
is Long -> createLongBinding(key, defaultValue as Long) as Binding<T?>
is Int -> createIntegerBinding(key, defaultValue as Int) as Binding<T?>
is Double -> createDoubleBinding(key, defaultValue as Double) as Binding<T?>
else -> throw IllegalArgumentException("Unknown type to create a binding for: " + defaultValue.javaClass.name)
}
}
private fun getPropertyWithFallback(key: String): String? {
// try to get the property from the current active locale
try {
resourcesRO.get()?.getProperty(key)
} catch (e: MissingResourceException) {
null
}?.run { return this }
// then try to find the property in the properties of the default locale
if (locale != null && defaultLocale != null && locale != defaultLocale) {
var rb = localizedResources[defaultLocale]
if (rb == null) {
rb = loadProperties(resourcePath, resourceFilePrefix, locale)
localizedResources[defaultLocale] = rb
}
try {
localizedResources[defaultLocale]?.getProperty(key)
} catch (e: MissingResourceException) {
null
}?.run { return this }
}
// finally attempt to find the property in the root locale
try {
localizedResources[Locale.ROOT]?.getProperty(key)
} catch (e: MissingResourceException) {
null
}?.run { return this }
return null
}
private fun createStringBinding(key: String, defaultValue: String?, vararg parameter: Any): StringBinding? {
return if (parameter.isEmpty()) {
var binding = keyStringBindings[key]
if (binding != null) return binding
binding = Bindings.createStringBinding(
{
if (defaultValue == null) getGuaranteedString(key).trim { it <= ' ' }
else getString(key) ?: defaultValue
},
resourcesRO
)
keyStringBindings[key] = binding
binding
} else {
Bindings.createStringBinding({
if (defaultValue == null)
getGuaranteedString(key, *parameter).trim { it <= ' ' }
else
String.format(locale, getString(key) ?: defaultValue, *parameter)
}, resourcesRO)
}
}
private fun createBooleanBinding(key: String, defaultValue: Boolean): BooleanBinding? {
var binding = keyBooleanBindings[key]
if (binding != null) return binding
binding = Bindings.createBooleanBinding({ getBoolean(key, defaultValue) }, resourcesRO)
keyBooleanBindings[key] = binding
return binding
}
private fun createIntegerBinding(key: String, defaultValue: Int): IntegerBinding? {
var binding = keyIntegerBindings[key]
if (binding != null) return binding
binding = Bindings.createIntegerBinding({ getInteger(key, defaultValue) }, resourcesRO)
keyIntegerBindings[key] = binding
return binding
}
private fun createLongBinding(key: String, defaultValue: Long): LongBinding? {
var binding = keyLongBindings[key]
if (binding != null) return binding
binding = Bindings.createLongBinding({ getLong(key, defaultValue) }, resourcesRO)
keyLongBindings[key] = binding
return binding
}
private fun createDoubleBinding(key: String, defaultValue: Double): DoubleBinding? {
var binding = keyDoubleBindings[key]
if (binding != null) return binding
binding = Bindings.createDoubleBinding({ getDouble(key, defaultValue) }, resourcesRO)
keyDoubleBindings[key] = binding
return binding
}
override fun dispose() {
keyStringBindings.clear()
keyBooleanBindings.clear()
keyIntegerBindings.clear()
keyLongBindings.clear()
keyDoubleBindings.clear()
}
private fun loadProperties(path: String, file: String, locale: Locale?): Properties? {
val fullPath = String.format(
"/%s/%s%s.properties",
path.trimStart('/').trimEnd('/'),
file,
if (locale == null || locale.language.isEmpty()) "" else "_${locale.language}",
)
if (this::class.java.getResource(fullPath) != null) {
try {
return this::class.java.getResourceAsStream(fullPath).let {
Properties().apply { load(InputStreamReader(it, StandardCharsets.UTF_8)) }
}
} catch (e: Exception) {
logger.severe("Could not load property resource file at: $fullPath", e)
}
} else {
logger.severe("Could not find property resource file at: $fullPath")
}
return null
}
companion object {
private val DEFAULT_PACKAGE_NAME = SimpleBaseResource::class.java.getPackage().name
private const val DEFAULT_PROPERTIES_NAME = "strings"
private val logger by lazy { createLogger() }
}
init {
require(resourcePath.isNotEmpty()) {
"The path with the strings.properties file must not be empty!"
}
require(resourceFilePrefix.isNotEmpty()) {
"The resource file prefix (e.g. 'strings' for 'strings.properties') must not be empty!"
}
bundleName = String.format("%s.%s", resourcePath, resourceFilePrefix)
val locale =
if (defaultLocale != null) Locale(defaultLocale.language.toLowerCase())
else Locale(Locale.getDefault().language.toLowerCase())
this.localeRO.set(locale)
if (localizedResources[locale] != null) {
resourcesRO.set(localizedResources[locale])
} else {
resourcesRO.set(loadProperties(resourcePath, resourceFilePrefix, locale))
localizedResources[locale] = resourcesRO.get()
}
// always fill the root locale data from the resource without language specification
localizedResources[Locale.ROOT] = loadProperties(resourcePath, resourceFilePrefix, Locale.ROOT)
parentResource = null
}
}
# src/main/resources/res
title=Title
# src/main/resources/res
title=German Title
dialog.ok=OK
dialog.cancel=Abbrechen
dialog.title=Dialog (de)
dialog.content=Das ist ein Dialog.
# src/main/resources/res
title=English Title
dialog.ok=OK
dialog.cancel=Cancel
dialog.title=Dialog (en)
dialog.content=This is a dialog.
package eu.dzim.kfxg.utils
import javafx.concurrent.Task
import java.io.BufferedReader
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.stream.Collectors
class SystemLanguageTask : Task<Locale?>() {
override fun call(): Locale? =
getSystemLanguage()
private fun getSystemLanguage(): Locale? =
System.getProperty("os.name")?.toLowerCase()
?.let {
when {
it.contains("windows") ->
if (System.getProperty("os.version").contains("10")) {
findLanguageWin10(handleProcess(CMD_LANG_WINDOWS_10))
} else {
findLanguageWinFallback(handleProcess(CMD_LANG_WINDOWS_FALLBACK))
}
it.contains("mac") -> findLanguageLinux(handleProcess(CMD_LANG_MAC))
it.contains("linux") -> findLanguageLinux(handleProcess(CMD_LANG_LINUX))
else -> null
}
}
private fun handleProcess(command: String): List<String> {
val process = Runtime.getRuntime().exec(command)
if (0 != process.waitFor()) return listOf()
process.waitFor()
@Suppress("UnnecessaryVariable")
val text = BufferedReader(
InputStreamReader(process.inputStream, StandardCharsets.UTF_8)
).lines().collect(Collectors.toList())
return text
}
}
package eu.dzim.kfxg.utils
import eu.dzim.kfxg.App
import eu.dzim.kfxg.res.resource
import java.io.BufferedReader
import java.io.File
import java.io.IOException
import java.io.InputStreamReader
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.logging.Level
import java.util.logging.LogManager
import java.util.logging.Logger
import java.util.stream.Collectors
private var logManagerInitialized = false
private val logManager = LogManager.getLogManager()?.apply {
if (!logManagerInitialized) {
synchronized(logManagerInitialized) {
getLogger(Logger.GLOBAL_LOGGER_NAME).level = Level.ALL
try {
readConfiguration(App::class.java.getResourceAsStream("/logging.properties"))
} catch (e: Exception) {
System.err.println("Problem while reading the log configuration: ${e.message}")
e.printStackTrace()
}
logManagerInitialized = true
}
}
}
fun Any.createLogger(): Logger {
logManager // only to force the log manager to be initialized
return Logger.getLogger(this::class.qualifiedName)
}
val Any.newLogger
get() = createLogger()
object WithLogger {
val logger by lazy { createLogger() }
}
fun Logger.severe(message: String, exception: Throwable) =
log(Level.SEVERE, message, exception)
fun Logger.warning(message: String, exception: Throwable) =
log(Level.WARNING, message, exception)
fun Logger.info(message: String, exception: Throwable) =
log(Level.INFO, message, exception)
fun Logger.fine(message: String, exception: Throwable) =
log(Level.FINE, message, exception)
fun printSystemProperties(logger: Logger? = null) {
arrayOf(
"file.separator", "java.class.path", "java.home", "java.vendor", "java.vendor.url", "java.version",
"line.separator", "os.arch", "os.name", "os.version", "path.separator", "user.dir", "user.home", "user.name",
"user.language", "user.country",
)
.map { "$it=${System.getProperty(it)}" }
.forEach {
if (logger == null) println(it)
else logger.fine(it)
}
}
fun handleProcess(command: String): List<String> {
val process = Runtime.getRuntime().exec(command)
if (0 != process.waitFor()) return listOf()
process.waitFor()
@Suppress("UnnecessaryVariable")
val text = BufferedReader(
InputStreamReader(process.inputStream, StandardCharsets.UTF_8)
).lines().collect(Collectors.toList())
return text
}
fun Array<String>.fireProcess(op: () -> File? = { null }): Process =
ProcessBuilder(*this).apply {
op()?.also { directory(it) }
environment()
redirectErrorStream(true)
// inheritIO()
}.start()
/**
* Takes this [String] as the key for the wrapped [eu.dzim.kfxg.res.Resource] translation utility.
*/
fun String.translate(vararg parameter: Any): String =
if (parameter.isNotEmpty()) resource.getGuaranteedString(this, *parameter)
else resource.getGuaranteedString(this)
const val CMD_LANG_WINDOWS_10 =
"cmd.exe /c powershell \"write-host(Get-WinSystemLocale | Select -ExpandProperty \"TwoLetterISOLanguageName\")\""
const val CMD_LANG_WINDOWS_FALLBACK = "cmd.exe /c \"systeminfo | findstr ;\""
const val CMD_LANG_MAC = "defaults read -g AppleLanguages"
const val CMD_LANG_LINUX = "sh -c env"
fun findLanguageWin10(text: List<String>): Locale? =
text.firstOrNull()
?.let { Locale(it) }
fun findLanguageWinFallback(text: List<String>): Locale? =
text.filterNot { it.isEmpty() || it.isBlank() }
.map { it.split(":")[1].trim() }
.map { it.split(";")[0].trim() }
.firstOrNull()
?.let { it.split("-")[0] }
?.let { Locale(it) }
fun findLanguageMac(text: List<String>): Locale? =
text.filterNot { it.contains("(") || it.contains(")") }
.map { it.trim().trimEnd(',').trim('"') }
.filterNot { it.isEmpty() }
.firstOrNull()
?.let { it.split("-")[0] }
?.let { Locale(it) }
fun findLanguageLinux(text: List<String>): Locale? =
text.firstOrNull { it.contains("LANG") }
?.let { it.split("=")[1].split("_")[0] }
?.let { Locale(it) }
fun downloadString(uri: URI): String? {
val client: HttpClient = HttpClient.newHttpClient()
val request = HttpRequest.newBuilder()
.uri(uri)
.build()
return try {
val result = client
.send(request, HttpResponse.BodyHandlers.ofString())
.body()
result
} catch (e: Exception) {
when (e) {
is IOException -> WithLogger.logger.severe("IO exception during the operation: ${e.message}")
else -> WithLogger.logger.severe("Something went wrong while performing the operation: ${e.message}")
}
null
}
}
@bgmf
Copy link
Author

bgmf commented Jan 21, 2021

Ignore that the App.kt became quite messy, but it's only to prove one point:

On Linux GraalVM's native-image will fail, when the UI-DSL's structure becomes to deep.


I experience the problem on a Laptop with Ubuntu 20.04 LTS. I use GraalVM 20.3.0, Community Edition.


The code maybe quite messy, actually it's derived from a very small test app, where I tried to play around with JavaFX and GraalVM to build a minimal running example on Linux and Windows, to showcase it to some colleagues at work.
In fact on Utils.kt I literally dumped a couple of functions to keep other files "as less ugly as possible". I may have failed, though. 🤷
And KotlinFX.kt... Well it borrowed very heavily from TornadoFX (which is a great library/framework), but the DSL is quite a thing.


Execute with
$ export GRAALVM_HOME=/home/daniel/<path-to>/graalvm-ce/; export JAVA_HOME=$GRAALVM_HOME
then proceed with
$ mvn clean client:build

The build will eventually fail with

[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------< eu.dzim:kotlin-javafx-graal >---------------------
[INFO] Building KotlinFX-Graal 0.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ kotlin-javafx-graal ---
[INFO] Deleting /home/daniel/git/github/graalfxtest/target
[INFO] 
[INFO] --------------------< eu.dzim:kotlin-javafx-graal >---------------------
[INFO] Building KotlinFX-Graal 0.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- client-maven-plugin:0.1.35:build (default-cli) @ kotlin-javafx-graal ---
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------< eu.dzim:kotlin-javafx-graal >---------------------
[INFO] Building KotlinFX-Graal 0.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> client-maven-plugin:0.1.35:compile (default-cli) > process-classes @ kotlin-javafx-graal >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ kotlin-javafx-graal ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 5 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ kotlin-javafx-graal ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- kotlin-maven-plugin:1.4.21:compile (compile) @ kotlin-javafx-graal ---
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.util.ReflectionUtil (file:/home/daniel/.m2/repository/org/jetbrains/kotlin/kotlin-compiler/1.4.21/kotlin-compiler-1.4.21.jar) to method java.util.ResourceBundle.setParent(java.util.ResourceBundle)
WARNING: Please consider reporting this to the maintainers of com.intellij.util.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[WARNING] /home/daniel/git/github/graalfxtest/src/main/kotlin/eu/dzim/kfxg/fx/KotlinFX.kt: (91, 21) The expression is unused
[INFO] 
[INFO] <<< client-maven-plugin:0.1.35:compile (default-cli) < process-classes @ kotlin-javafx-graal <<<
[INFO] 
[INFO] 
[INFO] --- client-maven-plugin:0.1.35:compile (default-cli) @ kotlin-javafx-graal ---
[Do. Jan. 21 16:53:17 MEZ 2021][INFO] ==================== COMPILE TASK ====================
             _______  ___      __   __  _______  __    _
            |       ||   |    |  | |  ||       ||  |  | |
            |    ___||   |    |  | |  ||   _   ||   |_| |
            |   | __ |   |    |  |_|  ||  | |  ||       |
            |   ||  ||   |___ |       ||  |_|  ||  _    |
            |   |_| ||       ||       ||       || | |   |
            |_______||_______||_______||_______||_|  |__|

    Access to the latest docs, tips and tricks and more info on
    how to get support? Register your usage of Gluon Substrate now at

    https://gluonhq.com/activate



[Do. Jan. 21 16:53:18 MEZ 2021][INFO] We will now compile your code for x86_64-linux-linux. This may take some time.
[Do. Jan. 21 16:53:22 MEZ 2021][INFO] [SUB] [eu.dzim.kfxg.mainkt:107669]    classlist:   2,422.83 ms,  0.96 GB
[Do. Jan. 21 16:53:24 MEZ 2021][INFO] [SUB] [eu.dzim.kfxg.mainkt:107669]        (cap):     679.22 ms,  0.96 GB
[Do. Jan. 21 16:53:25 MEZ 2021][INFO] [SUB] [eu.dzim.kfxg.mainkt:107669]        setup:   2,434.34 ms,  0.96 GB
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB] [eu.dzim.kfxg.mainkt:107669]     analysis:  26,647.17 ms,  3.22 GB
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB] Fatal error:com.oracle.graal.pointsto.util.AnalysisError$ParsingError: Error encountered while parsing eu.dzim.kfxg.App$start$3$1$1$1$7$1$1$$special$$inlined$apply$lambda$1.invoke(javafx.scene.layout.HBox) 
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB] Parsing context:
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing eu.dzim.kfxg.App$start$3$1$1$1$7$1$1$$special$$inlined$apply$lambda$1.invoke(App.kt:22)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing eu.dzim.kfxg.fx.KotlinFXKt.hbox(KotlinFX.kt:233)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing eu.dzim.kfxg.fx.KotlinFXKt.hbox$default(KotlinFX.kt:48)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing eu.dzim.kfxg.App.start(App.kt:78)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing com.sun.javafx.application.LauncherImpl$$Lambda$587/0x00000007c1266c40.run(Unknown Source)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing java.lang.Shutdown.runHooks(Shutdown.java:130)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing java.lang.Shutdown.shutdown(Shutdown.java:186)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing com.oracle.svm.core.jdk.RuntimeSupport.shutdown(RuntimeSupport.java:132)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:171)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:182)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     parsing com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB] 
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.util.AnalysisError.parsingError(AnalysisError.java:138)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:327)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:302)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:103)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.DefaultAnalysisPolicy$DefaultSpecialInvokeTypeFlow.onObservedUpdate(DefaultAnalysisPolicy.java:368)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.TypeFlow.notifyObservers(TypeFlow.java:470)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.TypeFlow.update(TypeFlow.java:542)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:547)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:173)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB] Caused by: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: java.lang.NoClassDefFoundError: eu/dzim/kfxg/App$start$3$1$1$1$7$1$1$1$5
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at parsing eu.dzim.kfxg.App$start$3$1$1$1$7$1$1$$special$$inlined$apply$lambda$1.invoke(App.kt:120)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.throwParserError(BytecodeParser.java:2566)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.throwParserError(SharedGraphBuilderPhase.java:96)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3404)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:3206)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:1092)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:986)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:84)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase.run(SharedGraphBuilderPhase.java:70)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.run(Phase.java:49)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:212)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:223)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:357)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:313)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     ... 13 more
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB] Caused by: java.lang.NoClassDefFoundError: eu/dzim/kfxg/App$start$3$1$1$1$7$1$1$1$5
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.lang.Class.getEnclosingMethod0(Native Method)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.lang.Class.hasEnclosingMethodInfo(Class.java:1708)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.lang.Class.isLocalOrAnonymousClass(Class.java:1704)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at java.base/java.lang.Class.isLocalClass(Class.java:1656)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJDKReflection.isLocalClass(HotSpotJDKReflection.java:94)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotResolvedObjectTypeImpl.isLocal(HotSpotResolvedObjectTypeImpl.java:967)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.meta.AnalysisType.isLocal(AnalysisType.java:1021)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.svm.hosted.SVMHost.createHub(SVMHost.java:379)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.svm.hosted.SVMHost.registerType(SVMHost.java:249)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.meta.AnalysisUniverse.createType(AnalysisUniverse.java:272)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookupAllowUnresolved(AnalysisUniverse.java:210)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookup(AnalysisUniverse.java:187)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at com.oracle.graal.pointsto.infrastructure.AnalysisConstantPool.lookupField(AnalysisConstantPool.java:48)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupField(BytecodeParser.java:4314)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.genGetStatic(BytecodeParser.java:4868)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5354)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3399)
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB]     ... 25 more
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] [SUB] Error: Image build request failed with exit status 1
[Do. Jan. 21 16:53:52 MEZ 2021][SEVERE] Process compile failed with result: 1
Check the log files under /home/daniel/git/github/graalfxtest/target/client/x86_64-linux/gvm/log
And please check https://docs.gluonhq.com/client/ for more information.
[Do. Jan. 21 16:53:52 MEZ 2021][INFO] Logging process [compile] to file: /home/daniel/git/github/graalfxtest/target/client/log/process-compile-1611244432307.log
[Do. Jan. 21 16:53:52 MEZ 2021][SEVERE] Compiling failed.
Check the log files under /home/daniel/git/github/graalfxtest/target/client/x86_64-linux/gvm/log
And please check https://docs.gluonhq.com/client/ for more information.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 48.467 s
[INFO] Finished at: 2021-01-21T16:53:52+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal com.gluonhq:client-maven-plugin:0.1.35:compile (default-cli) on project kotlin-javafx-graal: Compiling failed -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 50.604 s
[INFO] Finished at: 2021-01-21T16:53:52+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal com.gluonhq:client-maven-plugin:0.1.35:build (default-cli) on project kotlin-javafx-graal: Error, client:build failed -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

target/client/log/process-compile-.log

Process
=======
compile

Command Line
============
/home/daniel/programs/java/graalvm/graalvm-ce/bin/native-image -Djdk.internal.lambda.eagerlyInitialize=false --no-server -H:+ExitAfterRelocatableImageWrite -H:+SharedLibrary -H:+AddAllCharsets -H:+ReportExceptionStackTraces -H:-DeadlockWatchdogExitOnTimeout -H:DeadlockWatchdogInterval=0 -H:+RemoveSaturatedTypeFlows --features=org.graalvm.home.HomeFinderFeature -H:TempDirectory=/home/daniel/git/github/graalfxtest/target/client/x86_64-linux/gvm/tmp -H:EnableURLProtocols=http,https -H:ReflectionConfigurationFiles=/home/daniel/git/github/graalfxtest/target/client/x86_64-linux/gvm/reflectionconfig-x86_64-linux.json -H:JNIConfigurationFiles=/home/daniel/git/github/graalfxtest/target/client/x86_64-linux/gvm/jniconfig-x86_64-linux.json -H:ResourceConfigurationFiles=/home/daniel/git/github/graalfxtest/target/client/x86_64-linux/gvm/resourceconfig-x86_64-linux.json -H:IncludeResourceBundles=res.strings,res.strings_de,res.strings_en,com/sun/javafx/scene/control/skin/resources/controls,com/sun/javafx/scene/control/skin/resources/controls-nt,com.sun.javafx.tk.quantum.QuantumMessagesBundle -Dsvm.platform=org.graalvm.nativeimage.Platform$LINUX_AMD64 -cp /tmp/classpathJar2890411433204467879.jar eu.dzim.kfxg.MainKt

Output
======
[eu.dzim.kfxg.mainkt:107669]    classlist:   2,422.83 ms,  0.96 GB
[eu.dzim.kfxg.mainkt:107669]        (cap):     679.22 ms,  0.96 GB
[eu.dzim.kfxg.mainkt:107669]        setup:   2,434.34 ms,  0.96 GB
[eu.dzim.kfxg.mainkt:107669]     analysis:  26,647.17 ms,  3.22 GB
Fatal error:com.oracle.graal.pointsto.util.AnalysisError$ParsingError: Error encountered while parsing eu.dzim.kfxg.App$start$3$1$1$1$7$1$1$$special$$inlined$apply$lambda$1.invoke(javafx.scene.layout.HBox) 
Parsing context:
	parsing eu.dzim.kfxg.App$start$3$1$1$1$7$1$1$$special$$inlined$apply$lambda$1.invoke(App.kt:22)
	parsing eu.dzim.kfxg.fx.KotlinFXKt.hbox(KotlinFX.kt:233)
	parsing eu.dzim.kfxg.fx.KotlinFXKt.hbox$default(KotlinFX.kt:48)
	parsing eu.dzim.kfxg.App.start(App.kt:78)
	parsing com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
	parsing com.sun.javafx.application.LauncherImpl$$Lambda$587/0x00000007c1266c40.run(Unknown Source)
	parsing java.lang.Shutdown.runHooks(Shutdown.java:130)
	parsing java.lang.Shutdown.shutdown(Shutdown.java:186)
	parsing com.oracle.svm.core.jdk.RuntimeSupport.shutdown(RuntimeSupport.java:132)
	parsing com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:171)
	parsing com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:182)
	parsing com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)

	at com.oracle.graal.pointsto.util.AnalysisError.parsingError(AnalysisError.java:138)
	at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:327)
	at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:302)
	at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:103)
	at com.oracle.graal.pointsto.DefaultAnalysisPolicy$DefaultSpecialInvokeTypeFlow.onObservedUpdate(DefaultAnalysisPolicy.java:368)
	at com.oracle.graal.pointsto.flow.TypeFlow.notifyObservers(TypeFlow.java:470)
	at com.oracle.graal.pointsto.flow.TypeFlow.update(TypeFlow.java:542)
	at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:547)
	at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:173)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Caused by: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: java.lang.NoClassDefFoundError: eu/dzim/kfxg/App$start$3$1$1$1$7$1$1$1$5
	at parsing eu.dzim.kfxg.App$start$3$1$1$1$7$1$1$$special$$inlined$apply$lambda$1.invoke(App.kt:120)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.throwParserError(BytecodeParser.java:2566)
	at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.throwParserError(SharedGraphBuilderPhase.java:96)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3404)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:3206)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:1092)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:986)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:84)
	at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase.run(SharedGraphBuilderPhase.java:70)
	at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.run(Phase.java:49)
	at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:212)
	at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
	at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
	at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:223)
	at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:357)
	at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:313)
	... 13 more
Caused by: java.lang.NoClassDefFoundError: eu/dzim/kfxg/App$start$3$1$1$1$7$1$1$1$5
	at java.base/java.lang.Class.getEnclosingMethod0(Native Method)
	at java.base/java.lang.Class.hasEnclosingMethodInfo(Class.java:1708)
	at java.base/java.lang.Class.isLocalOrAnonymousClass(Class.java:1704)
	at java.base/java.lang.Class.isLocalClass(Class.java:1656)
	at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotJDKReflection.isLocalClass(HotSpotJDKReflection.java:94)
	at jdk.internal.vm.ci/jdk.vm.ci.hotspot.HotSpotResolvedObjectTypeImpl.isLocal(HotSpotResolvedObjectTypeImpl.java:967)
	at com.oracle.graal.pointsto.meta.AnalysisType.isLocal(AnalysisType.java:1021)
	at com.oracle.svm.hosted.SVMHost.createHub(SVMHost.java:379)
	at com.oracle.svm.hosted.SVMHost.registerType(SVMHost.java:249)
	at com.oracle.graal.pointsto.meta.AnalysisUniverse.createType(AnalysisUniverse.java:272)
	at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookupAllowUnresolved(AnalysisUniverse.java:210)
	at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookup(AnalysisUniverse.java:187)
	at com.oracle.graal.pointsto.infrastructure.AnalysisConstantPool.lookupField(AnalysisConstantPool.java:48)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupField(BytecodeParser.java:4314)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.genGetStatic(BytecodeParser.java:4868)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5354)
	at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3399)
	... 25 more
Error: Image build request failed with exit status 1


Result
======
result: 1

Other logs look basically the same, not much to see, except the exception...

@bgmf
Copy link
Author

bgmf commented Jan 25, 2021

Update: While I've used the older version of GraalVM 20.3.0, the same error unfortunately occurs with the current 21.0.0.

@bgmf
Copy link
Author

bgmf commented Feb 5, 2021

The issue for this problem can be found here (#3143).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment