Please view the edits below
I\'m trying to create a JShell instance that gives me access to, and lets me interact with objects in the <
only speaking to a small part of this rather substantial question:
Additionally, exchanging DirectExecutionControl with LocalExecutionControl gives the same results, but I do not understand the difference between the two classes
LocalExecutionControl extends DirectExecutionControl
and it overrides only invoke(Method method)
, the bodies of which are ...
local:
Thread snippetThread = new Thread(execThreadGroup, () -> {
...
res[0] = doitMethod.invoke(null, new Object[0]);
...
});
direct:
Object res = doitMethod.invoke(null, new Object[0]);
so the difference between the two classes is that direct invokes the method in the current thread, and local invokes it in a new thread. the same classloader is used in both cases, so you'd expect the same results in terms of sharing memory and loaded classes
Now, there is better and easier solution:
package ur.pkg;
import jdk.jshell.JShell;
import jdk.jshell.execution.LocalExecutionControlProvider;
public class TestShell {
public static int testValue = 5;
public static void main(String[] args) {
JShell shell = JShell.builder().executionEngine(new LocalExecutionControlProvider(), null).build();
TestShell.testValue++;
System.out.println(TestShell.testValue);
shell.eval("ur.pkg.TestShell.testValue++;").forEach(p -> {
System.out.println(p.value());
});
System.out.println(TestShell.testValue);
}
}
Default execution engine is JDI, but u can switch it to local or own.
The solution is to create a custom LoaderDelegate
implementation, that supplies instances of already loaded classes instead of loading them again. A simple example is to use the default implementation, DefaultLoaderDelegate
(source) and override the findClass
method of its internal RemoteClassLoader
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = classObjects.get(name);
if (b == null) {
Class<?> c = null;
try {
c = Class.forName(name);//Use a custom way to load the class
} catch(ClassNotFoundException e) {
}
if(c == null) {
return super.findClass(name);
}
return c;
}
return super.defineClass(name, b, 0, b.length, (CodeSource) null);
}
To create a working JShell instance, use the following code
JShell shell = JShell.builder()
.executionEngine(new ExecutionControlProvider() {
@Override
public String name() {
return "name";
}
@Override
public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
return new DirectExecutionControl(new CustomLoaderDelegate());
}
}, null)
.build();
shell.addToClasspath("Example.jar");//Add custom classes to Classpath, otherwise they can not be referenced in the JShell