Adventures in GraalVM: polyglot Camel routes with native-image

I’ve recently been playing with Camel to leverage JavaScript to define routes[1] and GraalVM to invoke JavaScript from a native image[2] so let’s try to make them working together!

As GraalVM as of RC6 does not (yet) support reflective access from scripting context back to Java world we need to use some proxy object GraalVM SDK provides, something like:

private Proxy createRouteDefinitionProxy(RouteDefinition def) {
    Map<String, Object> methods = new HashMap<>();

    methods.put("to", (ProxyExecutable) args -> {
        if (args.length != 1) {
            throw new IllegalArgumentException("");
        }

        // wrap the definition with a new
        // proxy
        return createRouteDefinitionProxy(
            def.to(args[0].asString())
        );
    });

    methods.put("setBody", (ProxyExecutable) args -> {
        if (args.length != 1) {
            throw new IllegalArgumentException("");
        }

        // assuming we only use strings in js
        def.setBody().constant(args[0].asString());

        // wrap the definition with a new
        // proxy
        return createRouteDefinitionProxy(def);
    });

    methods.put("setHeader", (ProxyExecutable) args -> {
        if (args.length != 2) {
            throw new IllegalArgumentException("");
        }

        // assuming we only use strings in js
        final String key = args[0].asString();
        final String val = args[0].asString();

        def.setHeader(key).constant(val);

        // wrap the definition with a new
        // proxy
        return createRouteDefinitionProxy(def);
    });

    return ProxyObject.fromMap(methods);
}

Now, let’s write a simple JavaScript route:

from('timer:js')
    .setBody('test')
    .to('log:js')

And finally, let’s run it:

$ ./target/org.apache.camel.graalvm.main src/main/resources/route.js
[main] INFO org.apache.camel.graalvm.FastCamelContext - Apache Camel 2.23.0-SNAPSHOT (CamelContext: camel-1) is starting
[main] INFO org.apache.camel.graalvm.FastCamelContext - StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
[main] INFO org.apache.camel.graalvm.FastCamelContext - Route: route1 started and consuming from: timer://js
[main] INFO org.apache.camel.graalvm.FastCamelContext - Total 1 routes, of which 1 are started
[main] INFO org.apache.camel.graalvm.FastCamelContext - Apache Camel 2.23.0-SNAPSHOT (CamelContext: camel-1) started in 0.001 seconds
[Camel (camel-1) thread #0 - timer://js] INFO js - Exchange[ExchangePattern: InOnly, BodyType: String, Body: test]
[Camel (camel-1) thread #0 - timer://js] INFO js - Exchange[ExchangePattern: InOnly, BodyType: String, Body: test]
[Camel (camel-1) thread #0 - timer://js] INFO js - Exchange[ExchangePattern: InOnly, BodyType: String, Body: test]
[Camel (camel-1) thread #0 - timer://js] INFO js - Exchange[ExchangePattern: InOnly, BodyType: String, Body: test]
[Camel (camel-1) thread #0 - timer://js] INFO js - Exchange[ExchangePattern: InOnly, BodyType: String, Body: test]
[Camel (camel-1) thread #0 - timer://js] INFO js - Exchange[ExchangePattern: InOnly, BodyType: String, Body: test]

Beside seing the route running, please note how fast the camel context has been started: 0.001 seconds

Full example can be found here

Caution

This is a very initial prototype so only a very limited subset of features has been opionantely implemented.