Jsr223 bindings


#1

Hi,

I’m trying to use kotlin as a scripting language for a jvm application through jsr223 and it works fine however there is some boilerplate to access bindings as they cannot be accessed directly but one ha to grab them from bindings, so as example, I do evaluate a kotlin script as follow:

final CamelContext context = getContext();
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByExtension("kts");
final Bindings bindings = new SimpleBindings();

// Exposed to the underlying script, but maybe better to have
// a nice dsl
bindings.put("builder", this);
bindings.put("context", context);
bindings.put("components", new Components(context));

try (InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, resource)) {
    engine.eval(new InputStreamReader(is), bindings);
}

Then the script looks like:

val builder = bindings["builder"] as org.apache.camel.builder.RouteBuilder
fun from(uri: String): org.apache.camel.model.RouteDefinition = builder.from(uri)

from("timer:tick")
    .process().message {
        m -> m.headers["MyHeader"] = "MyHeaderValue"
    }
    .to("log:info")

Is there any way to have bindings bounded to a global scope such as what’s possible when using Java’s nashorn interpreter or Groovy’s DelegatingScript ?

Thx


#2

One or the issues with JSR223 is that any form of such binding is non-standard. There is very little “advanced” support to be able to tighten integration even if it is not dependent on the actual language.


#3

Yeah but it would be really nice if kotlin provides some sort of support.

Another option would be to do something like Groovy’s delegating script


#4

My trick for getting around this is creating another class to hold bindings, then using with

data class ScriptBody(val context: CamelContext)

bindings.put("util", ScriptBody(...))
appendln("val util = bindings[\"util\"] as ${ScriptBody::class.qualifiedName}")
appendln("with(util) { $script }")

It’s messy but it works well enough for my purposes. I do agree there should be a better way to directly expose properties.


#5

I’m doing a similar thing by executing the initial stuffs in the same scripting context before the main script is evaluated, it works too but ugly,


#6

What actually “should” work (at least as far as JSR223 is concerned - I haven’t tried this in reality) is using the invoke operator. The problem is that the bindings are geared towards the javascript usecase only. It should be certainly possible for Kotlin to work on any value (exposed through a binding) that implements the invoke operator. It might also be possible for the Kotlin scripting system to automatically handle Kotlin (and Java) lambda’s as they have clear semantics. The main thing is you cannot expose functions, only objects. But you can create objects that forward to functions :wink: .