Class LocalContextExecutionControlProvider

java.lang.Object
org.dellroad.jct.jshell.LocalContextExecutionControlProvider
All Implemented Interfaces:
ExecutionControlProvider

public class LocalContextExecutionControlProvider extends Object implements ExecutionControlProvider
Same as LocalExecutionControlProvider, but with additional support for handling certain class path and class loading issues.

When executing JShell locally, it is often desirable that any classes visible to the thread that starts JShell should also be visible to any scripts and command inputs given to that JShell instance.

Unfortunately, this doesn't always happen automatically when using the standard "local" execution provided by LocalExecutionControl.

When executing JShell locally, there are two class path/loading issues to worry about:

  • When JShell compiles source snippets, what classes are available on the "source class path"? That is, what class names can you refer to by name in your scripts or snippets?
  • When a compiled script or snippet is loaded as a Java class file by the execution engine, what classes are available on the "binary class path" when resolving symbolic references?

The standard LocalExecutionControl requires non-standard classes to be explicitly added via the --class-path command line flag. Moreover, the ClassLoader that is uses delegates to the system class loader, which means that in certain more complex class loading scenarios (for example, when running as a servlet in the Tomcat web container), compiled snippets classes will fail to load due to resolution errors.

This class tries to workaround these issues as follows:

  • To address the "binary class path", this class uses a ClassLoader that delegates to the current thread's context class loader instead of the system class loader. This should fix linking problems in complex class loading scenarios.
  • To address the "souce class path", this class introspects the current thread's context class loader and its parents (recursively), attempting to glean what's on the class path. This works for any ClassLoader that are instances of URLClassLoader. However, Java's standard application class loader is not, so items on the JVM application class path are missed by this strategy. To include the application class loader, hacky instrospection relying on illegal accesses is attempted (failures are silently ignored). To ensure these efforts succeed, the following flag must be added to JVM startup: --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED. Note there is also another ugly workaround, which is to write JShell code that only accesses classes on the application class path via reflection.

To utilize this class, include the flags returned by modifyJShellFlags() as parameters to JShell startup.

This provider uses a MemoryLoaderDelegate.

See Also:
  • Field Details

  • Constructor Details

    • LocalContextExecutionControlProvider

      public LocalContextExecutionControlProvider()
  • Method Details

    • modifyJShellFlags

      public static void modifyJShellFlags(ClassLoader loader, List<String> flags)
      Modify a list of JShell tool command line flags to enable use of this class.
      Parameters:
      loader - loader to copy from, or null for the current thread's context class loader
      flags - modifiable list of command line flags
      Throws:
      IllegalArgumentException - if flags is null
    • getExecutionFlag

      public static String getExecutionFlag(List<String> flags)
      Get the execution provider name specified via the --execution flag, if any.
      Parameters:
      flags - list of command line flags
      Returns:
      execution provider name configured via --execution flag, or null if none
      Throws:
      IllegalArgumentException - if flags is null
      See Also:
    • setExecutionFlag

      public static void setExecutionFlag(List<String> flags, String providerName)
      Modify a list of JShell tool command line flags to force the use of the named execution provider, overriding any previous.
      Parameters:
      flags - modifiable list of command line flags
      providerName - execution provider name
      Throws:
      IllegalArgumentException - if flags is null
      See Also:
    • addToClassPath

      public static void addToClassPath(List<String> commandLine, List<String> components)
      Utility method to modify the given JShell command line flags to add/augment the --class-path flag(s) to (also) include the given classpath components.
      Parameters:
      commandLine - jshell command line, possibly including exisiting --class-path flag(s)
      components - new classpath components to add to commandLine
      Throws:
      IllegalArgumentException - if either parameter is null
    • name

      public String name()
      Specified by:
      name in interface ExecutionControlProvider
    • defaultParameters

      public Map<String,String> defaultParameters()
      Specified by:
      defaultParameters in interface ExecutionControlProvider
    • generate

      public ExecutionControl generate(ExecutionEnv env, Map<String,String> params)
      Specified by:
      generate in interface ExecutionControlProvider
    • createMemoryLoaderDelegate

      protected MemoryLoaderDelegate createMemoryLoaderDelegate(org.dellroad.stuff.java.MemoryClassLoader memoryLoader)
    • createMemoryClassLoader

      protected org.dellroad.stuff.java.MemoryClassLoader createMemoryClassLoader()
    • createLocalExecutionControl

      protected LocalExecutionControl createLocalExecutionControl(MemoryLoaderDelegate delegate)