Local execution - class loader & class path issues
Jan Lahoda
jan.lahoda at oracle.com
Tue Aug 15 13:12:00 UTC 2023
On 14. 08. 23 20:33, Archie Cobbs wrote:
> JShell experts,
>
> I encountered an issue working with JShell that brings up a few
> questions...
>
> Here's the story:
>
> * I have a running Java application. In my case, it's a web
> application running in Tomcat, but consider the general case.
> * 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.
> * 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)
> * Within JShell I want to be able to access my application's
> objects, e.g., "com.example.MyAppSingleton.getInstance()"
> o More precisely: I want any classes visible to the current
> thread's context class loader to be visible to JShell
>
> 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.
>
> Perhaps local execution is a bit of a neglected child... ?
>
> Here are some observations (please correct me if I'm wrong) and a few
> questions...
>
> #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 "com.example.MyClass" while the second can be tested with
> "Class.forName("com.example.MyClass")".
>
> #1: The --class-path flag affects both the compilation and the
> execution class paths with remote execution, but only the compilation
> classpath with local execution:
>
> $ ls classes/test/Foo.class
> classes/test/Foo.class
> $ jshell --execution=local --class-path classes
> | Welcome to JShell -- Version 20.0.1
> | For an introduction type: /help intro
>
> jshell> Thread.currentThread().getContextClassLoader();
> $1 ==>
> jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader at 45283ce2
>
> jshell>
> Arrays.asList(((URLClassLoader)Thread.currentThread().getContextClassLoader()).getURLs())
> $2 ==> []
>
> jshell> Class.forName("test.Foo");
> | Exception java.lang.ClassNotFoundException: test.Foo
>
> *Question*: Shouldn't LoaderDelegate.addToClasspath() have been passed
> the --class-path flag at some point in the example above? JShell's
> LocalExecutionControl uses DefaultLoaderDelegate, which uses a class
> loader of type DefaultLoaderDelegate.RemoteClassLoader. This would
> have added classes to RemoteClassLoader's class path.
Possibly.
>
> #2: The RemoteClassLoader 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:
>
> $ jshell --execution=local -J-classpath -Jclasses
> | Welcome to JShell -- Version 20.0.1
> | For an introduction type: /help intro
>
> jshell> Class.forName("test.Foo");
> $1 ==> class test.Foo
>
> 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.
>
> *Question*: Shouldn't DefaultLoaderDelegate$RemoteClassLoaderuse the
> current thread's context loader as its parent loader instead of
> defaulting to the system class loader?
If that was done from the beginning, maybe. Now, probably not, as that
would be an incompatible change. Should be possible to have a custom
ExecutionControl (an instance of or subtype of LocalExecutionControl),
with custom LoaderDelegate (where, possibly, there could be a factory
for DefaultLoaderDelegate taking a custom ClassLoader). Does not seem
this should be very too difficult?
(I am a bit confused about the command line example - at least the
"agent" (i.e. the DirectExecutionControl/LocalExecutionControl) must run
inside your target VM.)
>
> #3: Solving problem #2 would allow the execution class path to be
> properly configuredin any generic situation (that is,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?
>
> This is easy if the class loader is an URLClassLoader, which most are.
> Unfortunately the system class loader is not.
>
> *Question*: 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 URLClassLoader 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
> jdk.internal.loader.BuiltinClassLoader, which has an internal
> URLClassPath field.
Sorry, I don't think so. There has been some thinking some time ago on
supporting compilation based on ClassLoaders, but it is fairly tricky,
and very easy. Having heuristics inside JShell to read some data from
some ClassLoaders does not sounds like a very good idea to me, sorry.
Jan
>
> Or fix this in the JDK by defining some new interface that both
> URLClassLoader and BuiltinClassLoadercould implement:
>
> public interface HasUrlPath {
> URL[] getURLs();
> }
>
> Or: create a JavaFileManager implementation that wraps a ClassLoader
> (it's been done), and modify JShell to allow configuration of an
> alternate JavaFileManager (we can already do with JShell.Builder but
> not yet with JavaShellToolBuilder... why not?).
>
> Thanks for any thoughts & opinions on these questions.
>
> -Archie
>
> --
> Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/kulla-dev/attachments/20230815/66485dfa/attachment.htm>
More information about the kulla-dev
mailing list