Skip to content

Commit 3586d98

Browse files
committed
WIP
1 parent eca4d59 commit 3586d98

File tree

7 files changed

+114
-24
lines changed

7 files changed

+114
-24
lines changed

application/build.gradle.kts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ val kotlinVersion = "2.0.21"
1515

1616
val kotlinCompilerDependency = configurations.create("kotlinCompilerDependency")
1717
val kotlinCompilerDependencies = mutableListOf<String>()
18+
val versionOfSubmodules = "0.0.1"
19+
// All submodules must be unpacked with direct usage in scripts:
20+
val unpackSubmodules = listOf("business") // business must be unpacked, because KotlinScriptContext is used by scripts.
1821

1922
dependencies {
20-
implementation(project(":business"))
23+
unpackSubmodules.forEach { module ->
24+
implementation(project(":$module"))
25+
}
2126
// Spring Boot
2227
implementation("org.springframework.boot:spring-boot-starter")
2328
implementation("org.springframework.boot:spring-boot-starter-web")
@@ -46,13 +51,16 @@ dependencies {
4651
testImplementation("org.springframework.boot:spring-boot-starter-test")
4752
}
4853

49-
val kotlinCompilerDependencyFiles = kotlinCompilerDependency.map { it.name }
54+
val kotlinCompilerDependencyFiles = kotlinCompilerDependency.map { it.name } + unpackSubmodules.map { "$it-$versionOfSubmodules.jar" }
5055
tasks.named<BootJar>("bootJar") {
5156
// println(kotlinCompilerDependencyFiles.joinToString())
5257
exclude(kotlinCompilerDependencyFiles.map { "**/$it" })
5358
}
5459

5560
tasks.withType<Jar> {
61+
unpackSubmodules.forEach { module ->
62+
dependsOn(":$module:jar")
63+
}
5664
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
5765
from({
5866
configurations.runtimeClasspath.get().filter {

application/src/main/kotlin/de/micromata/kotlinscripting/DemoApplication.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package de.micromata.kotlinscripting
22

3+
import org.jetbrains.kotlin.ir.types.IdSignatureValues.result
34
import org.springframework.boot.autoconfigure.SpringBootApplication
45
import org.springframework.boot.runApplication
6+
import kotlin.script.experimental.api.ResultValue
57
import kotlin.script.experimental.api.ResultWithDiagnostics
68
import kotlin.script.experimental.api.valueOrNull
79

@@ -15,13 +17,21 @@ class DemoApplication {
1517

1618
val scriptExecutor = ScriptExecutor()
1719
val result = scriptExecutor.executeScript()
18-
if (result is ResultWithDiagnostics<*>) {
19-
println("Script result: ${result.valueOrNull()}")
20+
if (result is ResultWithDiagnostics.Success) {
21+
val returnValue = result.valueOrNull()?.returnValue
22+
val output = if (returnValue is ResultValue.Value) {
23+
returnValue.value
24+
} else {
25+
returnValue
26+
}
27+
println("Script result with success: ${output}")
28+
} else if (result is ResultWithDiagnostics<*>) {
29+
println("*** Script result: ${result.valueOrNull()}")
2030
result.reports.forEach {
2131
println("Script report: ${it.message}")
2232
}
2333
} else {
24-
println("Script result: $result")
34+
println("*** Script result: $result")
2535
}
2636
}
2737
}

application/src/main/kotlin/de/micromata/kotlinscripting/MyScriptingHost.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.micromata.kotlinscripting
22

3+
import de.micromata.kotlinscripting.business.ThreadLocalStorage
34
import mu.KotlinLogging
45
import kotlin.script.experimental.api.*
56
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
@@ -17,13 +18,14 @@ class CustomScriptingHost(
1718
): ResultWithDiagnostics<EvaluationResult> {
1819
val originalClassLoader = Thread.currentThread().contextClassLoader
1920
return try {
20-
// Setzen des gewünschten ClassLoaders
21-
Thread.currentThread().contextClassLoader = customClassLoader
22-
log.info { "CustomScriptingHost: Setting custom ClassLoader: $customClassLoader" }
21+
ThreadLocalStorage.threadLocal.set(Constants.THREADLOCAL_TEST)
22+
// Trying to set the custom ClassLoader here.
23+
// Thread.currentThread().contextClassLoader = customClassLoader
24+
// log.info { "CustomScriptingHost: Setting custom ClassLoader: $customClassLoader" }
2325
super.eval(script, compilationConfiguration, evaluationConfiguration)
2426
} finally {
25-
// Zurücksetzen des ursprünglichen ClassLoaders
26-
Thread.currentThread().contextClassLoader = originalClassLoader
27+
// Resetting the original ClassLoader:
28+
// Thread.currentThread().contextClassLoader = originalClassLoader
2729
}
2830
}
2931
}
Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
11
package de.micromata.kotlinscripting
22

33
import mu.KotlinLogging
4-
import java.io.File
54
import java.util.concurrent.Executors
65
import java.util.concurrent.Future
76
import java.util.concurrent.TimeUnit
87
import java.util.concurrent.TimeoutException
98
import kotlin.script.experimental.api.*
109
import kotlin.script.experimental.host.toScriptSource
11-
import kotlin.script.experimental.jvm.JvmDependencyFromClassLoader
1210
import kotlin.script.experimental.jvm.baseClassLoader
11+
import kotlin.script.experimental.jvm.dependenciesFromClassloader
1312
import kotlin.script.experimental.jvm.jvm
1413

1514
private val log = KotlinLogging.logger {}
1615

1716
class ScriptExecutor {
1817
private var evalException: Exception? = null
1918

20-
fun executeScript(): Any? {
19+
fun executeScript(): ResultWithDiagnostics<EvaluationResult>? {
2120
val classLoader = CustomClassLoader(Thread.currentThread().contextClassLoader)
2221

23-
//val scriptingHost = BasicJvmScriptingHost()
2422
val scriptingHost = CustomScriptingHost(classLoader)
25-
val dependencies =
26-
listOf(File("/Users/kai/workspace/Micromata/SpringBoot-KotlinScripting/build/libs/SpringBoot-KotlinScripting-0.0.1-SNAPSHOT.jar"))
2723
val compilationConfig = ScriptCompilationConfiguration {
2824
jvm {
29-
dependencies(JvmDependencyFromClassLoader { classLoader })
25+
dependenciesFromClassloader(classLoader = classLoader, wholeClasspath = true)
3026
//dependencies(JvmDependency(dependencies))
3127
//dependenciesFromClassloader(classLoader = classLoader, wholeClasspath = true)
3228
}
29+
providedProperties("context" to KotlinScriptContext::class)
30+
compilerOptions.append("-nowarn")
3331
}
32+
val context = KotlinScriptContext()
33+
context.setProperty("testVariable", Constants.TEST_VAR)
3434
val evaluationConfiguration = ScriptEvaluationConfiguration {
3535
jvm {
36-
//actualClassLoader(CustomClassLoader(Thread.currentThread().contextClassLoader))
3736
baseClassLoader(classLoader)
3837
}
38+
providedProperties("context" to context)
3939
}
4040
val scriptSource = script.toScriptSource()
4141
val executor = Executors.newSingleThreadExecutor()
@@ -44,7 +44,7 @@ class ScriptExecutor {
4444
future = executor.submit<ResultWithDiagnostics<EvaluationResult>> {
4545
scriptingHost.eval(scriptSource, compilationConfig, evaluationConfiguration)
4646
}
47-
return future.get(500, TimeUnit.SECONDS) // Timeout
47+
return future.get(10, TimeUnit.SECONDS) // Timeout
4848
} catch (ex: TimeoutException) {
4949
log.info("Script execution was cancelled due to timeout.")
5050
future?.cancel(true) // Attempt to cancel
@@ -61,17 +61,34 @@ class ScriptExecutor {
6161
}
6262

6363
companion object {
64+
val simplestScript = "\"Hello world!\""
6465
val script = """
65-
// Don't use StringBuilder here, because it's might not be available in the script classpath.
66-
var result = "Hello world!\n"
66+
import de.micromata.kotlinscripting.business.ThreadLocalStorage
67+
val sb = StringBuilder()
68+
sb.appendLine("Hello world!")
69+
val threadLocalVal = ThreadLocalStorage.threadLocal.get()
70+
if (threadLocalVal == "${Constants.THREADLOCAL_TEST}") {
71+
sb.appendLine("ThreadLocal: ${'$'}threadLocalVal (OK)")
72+
} else {
73+
sb.appendLine("ThreadLocal: ${'$'}threadLocalVal (*** ERROR, ${Constants.THREADLOCAL_TEST} expected)")
74+
}
75+
val testVar = context.getProperty("testVariable")
76+
if (testVar == "${Constants.TEST_VAR}") {
77+
sb.appendLine("Context: ${'$'}testVar (OK)")
78+
} else {
79+
sb.appendLine("Context: ${'$'}testVar (*** ERROR, ${Constants.TEST_VAR} expected)")
80+
}
6781
var loader = Thread.currentThread().contextClassLoader
82+
val classLoaders = mutableListOf<ClassLoader>()
6883
while (loader != null) {
69-
result += "ClassLoader: ${'$'}loader\n"
84+
classLoaders.add(loader)
7085
loader = loader.parent
7186
}
72-
result += "Classpath: ${'$'}{System.getProperty("java.class.path")}\n"
87+
sb.append("ClassLoader: ")
88+
sb.appendLine("${'$'}{classLoaders.joinToString(", parent: ") { it.toString() }}")
89+
sb.appendLine("Classpath: ${'$'}{System.getProperty("java.class.path")}")
7390
//sb.appendLine(de.micromata.springbootkotlinscripting.ScriptExecutor.script)
74-
result
91+
sb.toString()
7592
""".trimIndent()
7693
}
7794
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package de.micromata.kotlinscripting
2+
3+
object Constants {
4+
val THREADLOCAL_TEST = "Hello script! Greetings from ThreadLocal."
5+
val TEST_VAR = "Hello script! Greetings from testVar."
6+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Project ProjectForge Community Edition
4+
// www.projectforge.org
5+
//
6+
// Copyright (C) 2001-2024 Micromata GmbH, Germany (www.micromata.com)
7+
//
8+
// ProjectForge is dual-licensed.
9+
//
10+
// This community edition is free software; you can redistribute it and/or
11+
// modify it under the terms of the GNU General Public License as published
12+
// by the Free Software Foundation; version 3 of the License.
13+
//
14+
// This community edition is distributed in the hope that it will be useful,
15+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17+
// Public License for more details.
18+
//
19+
// You should have received a copy of the GNU General Public License along
20+
// with this program; if not, see http://www.gnu.org/licenses/.
21+
//
22+
/////////////////////////////////////////////////////////////////////////////
23+
24+
package de.micromata.kotlinscripting
25+
26+
/**
27+
* Context for bindings.
28+
*/
29+
class KotlinScriptContext {
30+
private val propertyValues = mutableMapOf<String, Any?>()
31+
32+
fun setProperty(name: String, value: Any?) {
33+
propertyValues[name] = value
34+
}
35+
36+
fun getProperty(name: String): Any? {
37+
return propertyValues[name]
38+
}
39+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package de.micromata.kotlinscripting.business
2+
3+
/**
4+
* ThreadLocalStorage object for testing access of ThreadLocal variables inside scripts.
5+
*/
6+
object ThreadLocalStorage {
7+
val threadLocal = ThreadLocal.withInitial { "Default Value" }
8+
}

0 commit comments

Comments
 (0)