Using Nashorn with interfaces loaded from custom classloaders

Attila Szegedi attila.szegedi at oracle.com
Wed Feb 11 08:56:36 UTC 2015


Java.type will only resolve names against script engine's "app loader" (which is whatever was the thread context loader at the time engine instance was created) and additionally from the path passed in as "-classpath" command line option (to get an engine with extra command line options programmatically you'll need to use NashornScriptEngineFactory class directly instead of only relying on javax.script.* APIs; actually you can pass explicit "app loader" with some of NashornScriptEngineFactory factory methods too).

In absence of this, you can do the following: if you pass in a Class object representing your interface (let's call it ForeignInterface) into the runtime somehow (e.g. through Bindings):

    bindings.put("foreignInterfaceClass", ForeignInterface.class)

then you can use "foreignInterfaceClass.static" to represent the static aspect of the type[1] in the script. Thereafter, 

    var ForeignInterface = foreignInterfaceClass.static;
    var adapter = Java.extend(ForeignInterface ...

should work, with the caveat that Nashorn's own classes must be visible through the class loader for ForeignInterface, as Nashorn will essentially be loading an adapter class that will have an "implements ForeignInterface" clause but it will also rely on some Nashorn internals in its bytecode, so it must be able to resolve symbolic bytecode references both to ForeignInterface and to some Nashorn classes. If your class loader has the extension class loader (which is where Nashorn lives) somewhere in its parent chain, you're good. Otherwise, if you have some more involved class loader topology, then I'm sorry to say that you're out of luck[2] :-(

As for your additional questions: 

#1 you can decide to not implement all methods of the interface (in fact, you could choose to implement none of them :-) ). If the underlying script object provides no implementation for an invoked method, then interface default method is invoked if one is provided by the interface, otherwise java.lang.UnsupportedOperationException is thrown.

#2 yes, javax.script has no explicit cleanup mechanism for script engines. It gets GC'd if it's unreachable. If you need resource cleanup tied to it not being reachable anymore, you'll need to use PhantomReference.

HTH,
  Attila.

---
[1] the one you would in Java simply call  "ForeignInterface", except in Java it's not a reified object, but purely a lexical language construct. In Nashorn, static types are reified, and you can think of ".static" as being the inverse operation of ".class"; .class on a static type gives you a runtime type (java.lang.Class) object, while .static on a j.l.Class object gives you back the static type which is used as both the constructor function for the type and a namespace for static fields/properties/methods (just as in Java). This whole discussion about static types makes me want to extend the Java.extend so that it accepts either a static type or a runtime j.l.Class object in its arguments, though.

[2] The truth is actually somewhat more complex; when Nashorn defines the adapter class, it does so in a small new class loader, and it needs to find a suitable parent for this class loader: one that sees the class being extended (if there's one), all the interface(s) being implemented, and the Nashorn classes themselves. This can get tricky if you're implementing multiple interfaces coming from multiple class loaders. Nashorn will choose a class loader from among all involved classes' and interfaces' class loaders that can see all the classes (the "maximum visibility class loader") by probing Class.forName from one loader with a name of a class from another loader and seeing if it resolves to the same class. If it finds one, it uses that as the parent loader for the adapter's loader. If it can't find one, you get a TypeError at runtime. You know I have a fun job when some days I wake up thinking "I need to write an algorithm for finding a maximum in a lattice of ClassLoader objects partially ordered by can-see-classes-in-the-other relation".

On Feb 6, 2015, at 10:05 AM, Christopher Brown <christopherbrown06 at gmail.com> wrote:

> Hello,
> 
> Is it possible to use the Java.type() then the Java.extend() syntax
> described here:
> https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions
> 
> ...with interface types loaded from a classloader other than the JRE
> classloader and the main application classloader.
> 
> As a simple example, suppose in the main() method of an application, I
> create a URL classloader to load arbitrary JAR files containing interface
> definitions, and then I create an instance of the Nashorn scripting
> engine.  Is it possible to make these types visible to the scripting
> engine, such that I could (in a loaded script) use Java.type() then
> Java.extend() to create an implementation of such a dynamically-loaded
> interface, and return it from the script when invoking a given function?
> 
> I'm guessing that it's not possible, because I've not seen this documented
> anywhere.  I would assume that it might be possible via the "context
> classloader" of the current thread, but I'd prefer not to rely on
> undocumented features (supposing that it's the case).  The most flexible
> approach would be to pass a classloader (or a map/array of classloaders)
> into the binding, and pass it as a second parameter to the Java.type()
> function (especially if in a more complicated example, multiple URL
> classloaders were loaded and defined types with the same fully-qualified
> names).
> 
> Additional question #1: would the implementation of the interface in
> Nashorn require that I implement default methods, or could they be invoked
> in the script without defining them?
> 
> Additional question #2: how should resources added to a binding, and more
> generally, resources used by a ScriptEngine instance, be cleaned up when
> the engine instance is no longer required?  For example, if adding
> classloaders is (or can be made) possible, I can't see any way to
> explicitly "close" the script engine... I'm assuming the only way to clean
> things up is to de-reference the engine and wait for it to be
> garbage-collected.
> 
> Thanks,
> Christopher



More information about the nashorn-dev mailing list