2015-07-28 5 views
4

Мне нужно вызвать (с помощью Nashorn) из Java-кода функцию, определенную в JavaScript, и передать там некоторые параметры. Вместо того чтобы использовать Invocable.invokeFunction ("Foo", arg1, arg2), я собирался определить интерфейс, а затем запросить Invocable производить его реализацию, как Oracle предлагает here, "Встраивание Oracle Насхорн":Nashorn JavaScript Invocable.getInterface терпит неудачу между загрузчиками классов в веб-приложении

package mypackage; 
public final class MyClass { 
    public interface Composer { 
    void compose(final StringBuilder subject, final StringBuilder body); 
    } 

    public void composeEmail(...) { 
     ... 
     final ScriptEngineManager engineManager = new ScriptEngineManager(); 
     final ScriptEngine engine = engineManager.getEngineByName("nashorn"); 
     engine.eval(scriptText); 
     final Invocable invocable = (Invocable) engine; 
     final Composer composer = (Composer)invocable.getInterface(Composer); 
     composer.compose(subject, body); 
     ... 
    } 
} 

Проблема в том, что я делаю это в веб-приложении, запущенном в Tomcat, мой Composer загружается загрузчиком классов приложений, а классы nashorn загружаются загрузчиком классов extensions. Так getInterface не говорит TypeError: Не удается найти общий загрузчик классов для ScriptObject и mypackage.Composer

Любые идеи о том, как преодолеть это? Я мог бы, конечно, попытаться загрузить Composer в родительском загрузчике классов, в предположении (хак-подобном), что он на самом деле является ext-загрузчиком, но есть хотя бы одна проблема с этим: он не может найти мой класс. Я полагаю, это совершенно правильно: мой пакет находится в моем веб-приложении, а загрузчик по расширению не выглядит там. Любые другие прекрасные идеи?

P.S. И теперь я заметил, что это сообщение странно: если класс-загрузчик приложений делегирует команду ext classloader, то, конечно, последний является для них общим загрузчиком классов. Может быть, они пытались сказать, что загрузчик классов целевых интерфейсов должен быть равен загрузчику классов реализационной реализации, или загрузчик классов реализации должен делегировать цель (но не наоборот)?

+0

Ваш P.S, вероятно, близок к проблеме. Не повторяйте классы для загрузчиков классов. Класс интерфейса может быть в ext classloader AND в загрузчике классов webapp, который имеет приоритет AFAIK. Не похож на проблему с кодом, более похожую на проблему «какие банки есть»;) – Kenney

ответ

1

Создать экземпляр jdk.nashorn.api.scripting.NashornScriptEngineFactory непосредственно вместо того, чтобы делать engineManager.engineByName(), а затем создать ScriptEngine, вызывая NashornScriptEngineFactory.getScriptEngine(ClassLoader appLoader) и передавая ему свой загрузчик приложений уровня.

+0

Извините, это дает тот же результат, что и раньше. Вы проверили это в Tomcat? Похоже, что класс ScriptObject от Nashorn загружается кем-то еще до моего вызова «factory.getScriptEngine (Composer.class.getClassLoader())», или цель параметра - указать, какой загрузчик использовать, когда JS хочет загрузить некоторые классы приложений (например, с построением объектов), а не указывать, какой загрузчик загрузит сам двигатель Nashorn. Я даже где-то читал, что Nashorn должен работать с повышенными привилегиями (что угодно) и, таким образом, всегда загружается загрузчиком расширения. –