Sharing dynamically loaded classes with JShell instance

后端 未结 3 677
不知归路
不知归路 2021-01-31 18:45

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 <

相关标签:
3条回答
  • 2021-01-31 18:49

    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

    0 讨论(0)
  • 2021-01-31 19:01

    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.

    0 讨论(0)
  • 2021-01-31 19:07

    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
    
    0 讨论(0)
提交回复
热议问题