So, I'm creating a scripting system using JSR 223
for Scala, but I came across this problem I can't find any cause for.
There is a singleton-like class, which has methods to add event listeners (from the scripts) and dispatch events (from the core). Everything worked fine, but for some reason the added listeners had disappeared when I got to dispatch an event.
After reproducing the problem, I found out that the script engine creates another instance of the singleton:
Here is my Singleton
class:
package test;
import java.util.Arrays;
public final class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
Arrays.stream(Thread.currentThread().getStackTrace()).forEach(System.out::println);
System.out.println();
}
public static Singleton instance() {
return instance;
}
}
And here is my Main
class:
package test;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public final class Main {
public static void main(String[] args) throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("scala");
// this is a trick I found to access the classpath,
// might be the problem
@SuppressWarnings("rawtypes")
scala.collection.immutable.List nil = scala.collection.immutable.Nil$.MODULE$;
@SuppressWarnings("unchecked")
scala.collection.immutable.$colon$colon<String> vals = scala.collection.immutable.$colon$colon$.MODULE$.apply("true", nil);
((scala.tools.nsc.interpreter.IMain) engine).settings().usejavacp().tryToSet(vals);
engine.eval("test.Singleton.instance");
Singleton.instance();
}
}
And here is the output:
java.lang.Thread.getStackTrace(Unknown Source)
test.Singleton.<init>(Singleton.java:10)
test.Singleton.<clinit>(Singleton.java:7)
$line3.$read$$iw$$iw$.<init>(<console>:8)
$line3.$read$$iw$$iw$.<clinit>(<console>)
$line3.$eval$.$result$lzycompute(<console>:5)
$line3.$eval$.$result(<console>:5)
$line3.$eval.$result(<console>)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:773)
scala.tools.nsc.interpreter.IMain$ReadEvalPrint.callEither(IMain.scala:777)
scala.tools.nsc.interpreter.IMain$ReadEvalPrint.evalEither(IMain.scala:792)
scala.tools.nsc.interpreter.IMain$WrappedRequest.eval(IMain.scala:613)
scala.tools.nsc.interpreter.IMain.eval(IMain.scala:1047)
javax.script.AbstractScriptEngine.eval(Unknown Source)
test.Main.main(Main.java:19)
java.lang.Thread.getStackTrace(Unknown Source)
test.Singleton.<init>(Singleton.java:10)
test.Singleton.<clinit>(Singleton.java:7)
test.Main.main(Main.java:20)
The stack trace shows that the script engine ends up creating a new instance of Singleton
, but I got no idea why.
Thank you.
Found out the fix after browsing the source code:
((scala.tools.nsc.interpreter.IMain) engine).settings().embeddedDefaults(Main.class.getClassLoader());
This changes the ClassLoader
for the ScriptEngine
to the same one.