Local execution - class loader & class path issues
Archie Cobbs
archie.cobbs at gmail.com
Mon Aug 14 18:33:51 UTC 2023
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()"
- 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 L
ocalExecutionControl uses DefaultLoaderDelegate, which uses a class loader
of type DefaultLoaderDelegate.RemoteClassLoader. This would have added
classes to RemoteClassLoader's class path.
#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$RemoteClassLoader use the
current thread's context loader as its parent loader instead of defaulting
to the system class loader?
#3: Solving problem #2 would allow the execution class path to be properly
configured in 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.
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/20230814/0021f1e1/attachment-0001.htm>
More information about the kulla-dev
mailing list