Invocable.getInterface fails across classloaders in web app
A. Sundararajan
sundararajan.athijegannathan at oracle.com
Tue Jul 28 16:21:40 UTC 2015
Nashorn uses thread context class loader to resolve "application
classes" (not the extension class loader).
If your class is not visible to nashorn, you can solve using two ways:
1) set the thread context class loader to be whichever loader that can
"see" those classes in question.
2) Using nashorn specific API - NashornScriptEngineFactory class and
directly pass a classloader (to be the "application loader" for nashorn)
while creating engine.
https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/NashornScriptEngineFactory.html
Thanks,
-Sundar
On Tuesday 28 July 2015 09:34 PM, Максим Гумеров 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