Search code examples
javanashorn

Nashorn CommandListener$$NashornJavaAdapter cannot be casted to the CommandListener


I'm using Nashorn with Java to make a modular plugin system. The plugins will be written in Javascript. An important feature is to have handlers that can be written in the Javascript code. Starting off I was aiming to make a simple command system. The javascript implements an interface, and calls a Java method to register the command. However, I am getting an error. I made sure that I am using Nashorn (var usingNashorn = typeof importClass !== "function";) returned true.

The Javascript:

var CommandListener = Java.extend(Java.type("com.techsdev.scriptengine.listeners.CommandListener"), {
    invoke : function(sender, command, args) {
        java.lang.System.out.println("Received a command: " + command);
    }
});

var listen = function(scriptManager) {
    var listener = new CommandListener();
    scriptManager.registerCommand("plugin name", "test", listener);
}

The Java code: To invoke "listen":

try {
    engine.eval(new FileReader(f));
    Invocable invocable = (Invocable) engine;
    invocable.invokeFunction("listen", this);
} catch(Exception e) {
    logger.error("Failed to load script "+f.getName(), e);
}

where 'f' is the Javascript file where 'engine' is the Nashorn ScriptEngine where 'this' is the ScriptManager

In the ScriptManager class this method is responsible to register the actual command:

public void registerCommand(String plugin, String command, CommandListener listener) {
        if(commandHandlers.containsKey(command.toLowerCase())) {
            logger.warn("Command "+command+" tried to be registered, but is already registered!");
            return;
        }

        commandHandlers.put(command.toLowerCase(), listener);
}

However, this code throws the following exception:

java.lang.ClassCastException: com.techsdev.scriptengine.listeners.CommandListener$$NashornJavaAdapter cannot be cast to com.techsdev.scriptengine.listeners.CommandListener
    at com.techsdev.scriptengine.JsScriptManager.registerCommand(JsScriptManager.java:168) ~[scriptengine-mod.jar:?]
    at jdk.nashorn.internal.scripts.Script$Recompilation$2$616A$\^eval\_.listen(<eval>:22) ~[?:?]
    at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:625) ~[nashorn.jar:?]
    at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494) ~[nashorn.jar:?]
    at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) ~[nashorn.jar:?]
    at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:199) ~[nashorn.jar:?]
    at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:383) ~[nashorn.jar:?]
    at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190) ~[nashorn.jar:?]
    at com.techsdev.scriptengine.JsScriptManager.loadFile(JsScriptManager.java:134) [JsScriptManager.class:?]
    at com.techsdev.scriptengine.JsScriptManager.loadFolder(JsScriptManager.java:116) [JsScriptManager.class:?]
    at com.techsdev.scriptengine.JsScriptManager.init(JsScriptManager.java:104) [JsScriptManager.class:?]

Please let me know if I missed anything. Thank you in advance.


Solution

  • I'd print the classloader of com.techsdev.scriptengine.listeners.CommandListener from script and as well as from Java code.

    From JavaScript:

    print(Java.type("com.techsdev.scriptengine.listeners.CommandListener").class.classLoader)
    

    From Java:

    System.out.println(com.techsdev.scriptengine.listeners.CommandListener.class.getClassLoader());
    

    If the same (fully-qualified) named class bytes is loaded by two different loaders, those (runtime) classes are different from JVM point of view. If you see different classloaders from javascript and java code, most likely you have a classpath/classloader issue.