Вызов функции в подклассе Java в Nashorn

У меня есть скрипт JavaScript, который выглядит примерно так:

function run(database) {
    var result = database.query("query", "some resource name");
    //operations on result
    return result;
}

и у меня есть код Java, который выполняет скрипт, который выглядит примерно так:

public Object execute(String script, Database database) {
    NashornScriptEngineFactory nsef = new NashornScriptEngineFactory();
    ScriptEngine engine = nsef.getScriptEngine();
    try {
        engine.eval(script);
        Invocable invocable = (Invocable) engine;
        return invocable.invokeFunction("run", database);
    } catch(ScriptException e) {
        throw new RuntimeException(e);
    }
}

Database — это интерфейс, который содержит определения нескольких методов, но не содержит метод query. Я вызываю execute с реализацией Database, назову ее DatabaseImpl, которая действительно имеет метод query. Это будет полиморфно, и ожидается, что скрипт знает, какие методы доступны для экземпляра Database, переданного ему. Я решил не использовать дженерики с этим, поскольку они стираются во время выполнения, и поэтому JavaScript все равно не может их использовать, поэтому автор сценария должен правильно определить типы.

Однако, когда я запускаю этот код, я получаю следующее исключение:

javax.script.ScriptException: TypeError: database.query is not a function in <eval> at line number 25

По сути, суть в том, что у меня есть объект, который реализует интерфейс, и вызываю метод, который реализует конкретный экземпляр, но не является частью определения интерфейса. У меня сложилось впечатление, что это все еще должно работать, но это не так. Для меня не имеет особого смысла, что мне нужно будет выполнить подкаст внутри скрипта, чтобы иметь доступ к методу query (возможно ли это вообще?), так почему я получаю эту ошибку? Это потому, что метод недоступен из определения интерфейса? Есть ли обходной путь?

Спасибо.


person Peter    schedule 12.04.2017    source источник


Ответы (1)


Это основной класс:

package so;
import java.io.InputStreamReader;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class Nashorn {
    public static void main(String[] args) {
        try (InputStreamReader in = resource()) {
            ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
            engine.eval(in);
            Invocable invocable = (Invocable) engine;
            Database database = new DatabaseImpl();
            Object x = invocable.invokeFunction("run", database);
            System.out.println(x);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static InputStreamReader resource() throws Exception {
        return new InputStreamReader(Nashorn.class.getResourceAsStream("db.js"), "utf-8");
    }

}

Интерфейс и реализация

package so;

public interface Database {
    void connect();
}

package so;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class DatabaseImpl implements Database {

    @Override
    public void connect() {
        System.out.println("Connecting");
    }

    public List<?> query(String ... stmt){
        List<String> lst = new ArrayList<>(); 
        lst.addAll(Arrays.asList(stmt));
        lst.addAll(Arrays.asList("A","B","C"));
        return lst;
    }

}

Файл javascript (so/db.js)

function run(database) {
    var result = database.query("query", "some resource name");
    //operations on result
    return result;
}

Запуск приводит к:

[query, some resource name, A, B, C]

Это в основном работает.

person minus    schedule 12.04.2017
comment
Тогда, наверное, PEBCAC... Буду искать дальше. Спасибо, что проверили это. - person Peter; 12.04.2017