Search code examples
javanashorn

Nashorn: How to pre-set Java.type()-vars inside of Java before JavaScript execution?


I am currently executing my JavaScript-scripts with this java code:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("awesome_script.js"));

I need to call Java functions from JavaScript, so I defined this at the top of my awesome_script.js file:

var first = Java.type('io.github.awesomeprogram.FirstClass');
var second = Java.type('io.github.awesomeprogram.SecondClass');
var extra = Java.type('io.github.awesomeprogram.ExtraClass');

I can then call some methods from these classes, e.g.:

second.coolmethod("arg1",2);

My problem is now that I need to use many java classes inside of my scripts. I also have a lot of scripts and I think it is very inefficient to define every single one of this classes in every script.

So I am looking for a solution to create the objects created inside of JavaScript with Java.type() inside of Java and then pass them to the script I want to execute.

How can I do this?

Thanks in advance!


Solution

  • After quite a bit of research I found a way to put global variables in the ScriptEngine before executing: The Java Scripting API (Oracle Docs)

    This enabled me to put any object I want into a global variable. However, I still needed a way to get the Object that Java.type() creates inside of Java. So I wrote a test script which returns one of this objects and I found out it is an object of the type jdk.internal.dynalink.beans.StaticClass. This class has an constructor which takes a ordinary Class as an argument. Sadly, this constructor is not usable in my code because it is not visible. To bypass this I used reflection and made this method:

    public StaticClass toNashornClass(Class<?> c) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
    
        Class<?> cl = Class.forName("jdk.internal.dynalink.beans.StaticClass");
    
        Constructor<?> constructor = cl.getDeclaredConstructor(Class.class);
    
        constructor.setAccessible(true);
        StaticClass o = (StaticClass) constructor.newInstance(c);
    
        return o;
    }
    

    If I pass the Class of the object I want as a global variable I just need to call toNashornClass(Example.class); and put the resulting object into a global var with engine.put("example",object);

    It works fine. I can use the example var completely like a var created by Java.type().