Invocable.getInterface fails across classloaders in web app
Attila Szegedi
attila.szegedi at oracle.com
Tue Jul 28 16:12:43 UTC 2015
Create an instance of jdk.nashorn.api.scripting.NashornScriptEngineFactory directly instead of doing engineManager.engineByName(), then create a ScriptEngine by invoking NashornScriptEngineFactory.getScriptEngine(ClassLoader appLoader) and passing it your app-level loader.
Hope that helps,
Attila.
(I’ll answer to StackOverflow too.)
> On Jul 28, 2015, at 6:04 PM, Максим Гумеров <mgumerov at gmail.com> wrote:
>
> Hello!
> Recently I posted a question at stackoverflow, so far no one had the
> answer, so I thought I might find one here.
>
> My original question is here:
> http://stackoverflow.com/questions/31669566/nashorn-javascript-invocable-getinterface-fails-across-classloaders-in-web-app
>
> For reader's comfort, I will reproduce its below.
> Thank you!
> Maksim
>
> ====
>
> I need to invoke (with Nashorn) from Java code a function defined in
> JavaScript and to pass there some parameters. Instead of using
> Invocable.invokeFunction("Foo", arg1, arg2), I was going to define an
> interface, then request Invocable to produce its implementation, just like
> Oracle suggests here
> <http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html%20%22here'>,
> "Embedding Oracle Nashorn":
>
> 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);
> ...
> }}
>
> Problem is, since I am doing this in a web application running in Tomcat,
> my Composer gets loaded by app-level classloader, while nashorn classes
> were loaded by extensions class loader. So getInterface fails saying a
> *TypeError:
> Can not find a common class loader for ScriptObject and mypackage.Composer*
>
> Any ideas how to overcome that? I could, of course, try to load Composer in
> a parent classloader, in an assumption (hack-like) that it is actually an
> ext loader, but there is at least one problem with that: it cannot find my
> class. I suppose it's quite right: my package resides in my web
> application, and extension loader does not look there. Any other great
> ideas?
>
> P.S. And now I noticed that this message is weird: if an app classloader
> delegates to ext classloader, then of course the latter is the common
> classloader for them. Maybe they were trying to say that the target
> interfaces's classloader must be equal to actual implementation's
> classloader, or the implementation's classloader must delegate to the
> target's (but not vice versa)?
More information about the nashorn-dev
mailing list