<div dir="ltr"><div>JShell experts,</div><div><br></div><div>I encountered an issue working with JShell that brings up a few questions...</div><br><div>Here's the story:</div><div><ul><li>I have a running Java application. In my case, it's a web application running in Tomcat, but consider the general case.</li><li>I want to be able to connect to my running Java application via SSH and get a command line interface (CLI) where I can do stuff.</li><li>One of the commands I'd like to have is a "jshell" command which would fire up a JShell instance within the CLI (using local execution)</li><li>Within JShell I want to be able to access my application's objects, e.g., "<span style="font-family:monospace">com.example.MyAppSingleton.getInstance()</span>"</li><ul><li>More precisely: I want any classes visible to the current thread's context class loader to be visible to JShell<br></li></ul></ul><div>The last step is the tricky one for me. It seems like a reasonable thing to want to do, but JShell seems to make it difficult.</div><div><br></div><div>Perhaps local execution is a bit of a neglected child... ?<br></div><div><br></div><div>Here are some observations (please correct me if I'm wrong) and a few questions...<br></div></div><div><br></div><div>#0: There are two separate "paths" to worry about with JShell: the compilation classpath and the execution class path. The first can be tested with "<span style="font-family:monospace">com.example.MyClass</span>" while the second can be tested with "<span style="font-family:monospace">Class.forName("com.example.MyClass")</span>".</div><div><br></div><div>#1: The <span style="font-family:monospace">--class-path</span> flag affects both the compilation and the execution class paths with remote execution, but only the compilation classpath with local execution:</div><div><br></div><div style="margin-left:40px"><span style="font-family:monospace">$ ls classes/test/Foo.class <br>classes/test/Foo.class</span></div><div style="margin-left:40px"><span style="font-family:monospace">$ jshell --execution=local --class-path classes<br>|  Welcome to JShell -- Version 20.0.1<br>|  For an introduction type: /help intro<br><br>jshell> Thread.currentThread().getContextClassLoader();<br>$1 ==> jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader@45283ce2<br><br>jshell> Arrays.asList(((URLClassLoader)Thread.currentThread().getContextClassLoader()).getURLs())<br>$2 ==> []<br><br>jshell> Class.forName("test.Foo");<br>|  Exception java.lang.ClassNotFoundException: test.Foo</span></div><div><div><br></div><div><b>Question</b>: Shouldn't <span style="font-family:monospace">LoaderDelegate.addToClasspath()</span> have been passed the <span style="font-family:monospace">--class-path</span> flag at some point in the example above? <span style="font-family:arial,sans-serif">JShell's L</span><span style="font-family:monospace">ocalExecutionControl</span> uses <span style="font-family:monospace">DefaultLoaderDelegate</span>, which uses a class loader of type <span style="font-family:monospace">DefaultLoaderDelegate.</span><span style="font-family:monospace">RemoteClassLoader</span>. This would have added <span style="font-family:monospace">classes</span> to <span style="font-family:monospace">RemoteClassLoader</span><span style="font-family:arial,sans-serif">'s class path.<br></span></div><div><span style="font-family:arial,sans-serif"><br></span></div><div><span style="font-family:arial,sans-serif">#2: The </span><span style="font-family:monospace">RemoteClassLoader</span> that has the system class loader as its parent loader, and this is not configurable. This fact combined with #1 means the execution class path is simply not configurable with local execution, except implicitly via JVM flags:</div><div><br></div><div><div style="margin-left:40px"><span style="font-family:monospace">$ jshell --execution=local -J-classpath -Jclasses</span><br><span style="font-family:monospace">|  Welcome to JShell -- Version 20.0.1</span><br><span style="font-family:monospace">|  For an introduction type: /help intro</span><br><span style="font-family:monospace"></span><br><span style="font-family:monospace">jshell> Class.forName("test.Foo");</span><br><span style="font-family:monospace">$1 ==> class test.Foo</span><br></div><div style="margin-left:40px"><span style="font-family:monospace"></span><br></div>If you are running in a non-trivial class loader environment (e.g., Tomcat), using JVM flags is not feasible and so your application classes can never be found in JShell.<br></div><div><br></div><div><b>Question</b>: Shouldn't <span style="font-family:monospace">DefaultLoaderDelegate$RemoteClassLoader</span><span style="font-family:arial,sans-serif"> use the current thread's context loader as its parent loader instead of defaulting to the system class loader?<br></span></div><div><span style="font-family:arial,sans-serif"><br></span></div><div><span style="font-family:arial,sans-serif">#3: Solving problem #2 would allow the execution class path to be properly configured</span><span style="font-family:arial,sans-serif"> in any generic situation (that is,</span><span style="font-family:arial,sans-serif"> from the context class loader). But we have the same problem for the compilation class path - so the corresponding question is: given a class loader, how do you extract the classpath that it's using?</span></div><div><span style="font-family:arial,sans-serif"><br></span></div><div><span style="font-family:arial,sans-serif">This is easy if the class loader is an </span><span style="font-family:monospace">URLClassLoader</span><span style="font-family:arial,sans-serif">, which most are. Unfortunately the system class loader is not.</span><br></div><div><br></div><div><b>Question</b>: Should JShell provide a way to "best effort" extract a compilation class path from a given ClassLoader (and its parents)? It could work for any <span style="font-family:monospace">URLClassLoader</span> instances, but being part of the JDK it could also take advantage of inter-module access to the system and other built-in class loaders, which are all of type <span style="font-family:monospace">jdk.internal.loader.BuiltinClassLoader</span>, which has an internal <span style="font-family:monospace">URLClassPath</span> field.</div><div><br></div><div>Or fix this in the JDK by defining some new interface that both <span style="font-family:monospace">URLClassLoader</span> and <span style="font-family:monospace">BuiltinClassLoader</span>could implement:<br></div></div><div><br></div><div style="margin-left:40px"><span style="font-family:monospace">public interface HasUrlPath {</span></div><div style="margin-left:40px"><span style="font-family:monospace">    URL[] getURLs();</span></div><div style="margin-left:40px"><span style="font-family:monospace">}<br></span></div><div><div><br></div><div>Or: create a <span style="font-family:monospace">JavaFileManager</span> implementation that wraps a <span style="font-family:monospace">ClassLoader</span> (it's been done), and modify JShell to allow configuration of an alternate <span style="font-family:monospace">JavaFileManager</span> (we can already do with <span style="font-family:monospace">JShell.Builder</span> but not yet with <span style="font-family:monospace">JavaShellToolBuilder</span>... why not?).<br></div><div><br></div><div>Thanks for any thoughts & opinions on these questions.<br></div><div><br></div><div><div>-Archie</div><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature">Archie L. Cobbs<br></div></div></div></div>