Class ScriptingContext

java.lang.Object
org.bzdev.scripting.ScriptingContext
Direct Known Subclasses:
AbstractSplinePathBuilder, DefaultScriptingContext, ExtendedScriptingContext, Simulation

public class ScriptingContext extends Object
Class for running scripts. A scripting context may also have a "parent" scripting context. This allows classes such as Simulation to be subclasses of ScriptingContext while an already configured scripting context's script engine and bindings.

There are several subclasses of ScriptingContext in this package. In particular, the class ExtendedScriptingContext, provides methods for importing Java classes into a scripting environment (these methods were added by the author out of frustration with the Rhino to Nashorn transition, where the default mechanism for importing java classes suddenly changed). The class DefaultScriptingContext will look up a scripting language by name, with the name provided in a constructor. The default is to use the ESP scripting language (added by the author after the "powers that be" decided to drop support for Nashorn and finding that GraalVM, the suggested alternative, did not provide adequate support for multithreading).

See Also:
  • Constructor Details

    • ScriptingContext

      public ScriptingContext()
      Constructor.
    • ScriptingContext

      @Deprecated public ScriptingContext(boolean trusted)
      Deprecated.
      trusted scripting contexts are not applicable when the security manager has been eliminated.
      Constructor specifying the security mode.
      Parameters:
      trusted - true if the script context is trusted; false otherwise
    • ScriptingContext

      public ScriptingContext(ScriptingContext parent)
      Constructor using a parent. Unless methods are overridden, the parent scripting context provides the scripting language, script engine, and bindings.
      Parameters:
      parent - the parent scripting context; null if there is none.
  • Method Details

    • usingGraalVM

      public static boolean usingGraalVM()
      Test if GraalVM is running. This method is provided because scripting languages don't work the same with GraalVM as with OpenJDK:
      • Immediately after a script engine is created, one should get the script engine's bindings and call bindings.put("polyglot.js.allowAllAccess", true) - otherwise attempts to call a Java method or construct a Java object will fail.
      • If the default security manager is installed, calling engine.get(ScriptEngine.FILENAME) will throw an exception unless protected by a doPrivileged block. Seen in GraalVM CE Java11-20.1.0.
      The test makes use of the system property java.vendor.version, which in GraalVM CE Java11-20.1.0 contains a string starting with GraalVM. OpenJDK does not define this system property.
      Returns:
      true if GraalVM appears to be running; false otherwise
    • createBindingSwapper

      protected ScriptingContext.BindingSwapper createBindingSwapper(Bindings bindings)
      Create a binding swapper. The use of this class is intended for the case where a script will be run in a separate thread and then suspend itself. When a scripting context runs a script, it puts its default bindings in an inherited thread-local variable. If new thread is created while that script is running, those bindings will be inherited. Then the bindings that a script will run can be passed to this method, and the binding swapper can be used to toggle between the two sets of bindings if the new thread is suspended or restored.

      The caller must ensure that the default bindings are in effect when threads making use of a binding swapper create additional threads that use scripting contexts to run scripts.

      Most applications should not need to create binding swappers. The class was created to support classes in the package org.bzdev.devqsim. The ScriptingContext class does not provide a public method that returns a script engine, nor a public method to set its bindings. Some methods such as evalScript, however, allow alternate bindings to be used, and will keep the old bindings on a stack, restoring those with the method exits. A binding swapper can be used to swap those temporary bindings with the script engine's default bindings. For example, the devqsim package has a class named TaskThread that uses a binding swapper to temporarily change the current bindings to the default bindings when a thread is being paused because the simulation may be running in a script and needs those original bindings for instrumentation-related tasks that occur as threads are suspended and restored.

      Parameters:
      bindings - the binding to initially swap with an existing binding when ScriptingContext.BindingSwapper.swap() is called
      Returns:
      the new binding swapper
    • createScriptEngine

      public static ScriptEngine createScriptEngine(ScriptEngineManager manager, String languageName)
      Create a script engine. This method is provided due to a security issue discovered with Javascript in which calling a ScriptEngineManager's method getEngineByName would result in elevated privileges if called after a security manager is installed. A test indicated that one can suppress this behavior by using AccessController.doPrivilege with a context that has no permissions. A bug report was filed (this was some time ago with a much earlier version of Java).

      This method is now used to allow some initialization when GraalVM is used so that specific properties can be set (otherwise GraalVM's ECMAScript implementation will be severely restricted - it will not be able to call Java methods or constructors.

      Parameters:
      manager - a ScriptEngineManager used to obtain the engine
      languageName - the name of the scripting language;
      Returns:
      the script engine created; null if none matches the specified scripting-language name
    • evalScript

      public final Object evalScript(Reader reader) throws ScriptException, UnsupportedOperationException
      Evaluate a script provided via a Reader.
      Parameters:
      reader - a Reader providing the script
      Returns:
      the value produced by the script
      Throws:
      ScriptException - an error occurred in the script
      NullPointerException - the script or bindings were null
      UnsupportedOperationException - the script engine is null
    • evalScript

      public final Object evalScript(String fileName, Reader reader) throws ScriptException, UnsupportedOperationException
      Evaluate a script provided via a Reader given the Reader's file name. The file name that the script engine uses is set temporarily and then restored to its previous value after the script is read.
      Parameters:
      fileName - the file name for the reader
      reader - a Reader providing the script
      Returns:
      the value produced by the script
      Throws:
      ScriptException - an error occurred in the script
      NullPointerException - the script or bindings were null
      UnsupportedOperationException - the script engine is null
    • evalScript

      public final Object evalScript(String script) throws ScriptException, UnsupportedOperationException
      Evaluate a script provided in a string.
      Parameters:
      script - the script to evaluate
      Returns:
      the value produced by the script
      Throws:
      ScriptException - an error occurred in the script
      NullPointerException - the script or bindings were null
      UnsupportedOperationException - the script engine is null
    • evalScript

      public final Object evalScript(String script, Bindings bindings) throws ScriptException, IllegalArgumentException
      Evaluate a script provided in a string using specified bindings.
      Parameters:
      script - the script to evaluate
      bindings - the script's bindings; null for the default bindings
      Returns:
      the value produced by the script
      Throws:
      ScriptException - an error occurred in the script
      IllegalArgumentException - an argument was null or the bindings were not created by this scripting context
    • callScriptFunction

      public final Object callScriptFunction(String functionName, Object... args) throws ScriptException, UnsupportedOperationException, NoSuchMethodException
      Evaluate a script function.
      Parameters:
      functionName - the script function to call
      args - the arguments
      Returns:
      the value computed by the function
      Throws:
      ScriptException - the function was not defined or matching arguments could not be found
      UnsupportedOperationException - there was no script engine to use
      NoSuchMethodException - the function does not exist or the arguments do not match
    • callScriptFunction

      public final Object callScriptFunction(Bindings bindings, String functionName, Object... args) throws ScriptException, IllegalArgumentException, NoSuchMethodException
      Evaluate a script function given a set of bindings.
      Parameters:
      bindings - the bindings to use when the script is evaluated; null for the default bindings
      functionName - the script function to call
      args - the arguments
      Returns:
      the value computed by the function
      Throws:
      ScriptException - the function was not defined or matching arguments could not be found
      IllegalArgumentException - an argument was null or the bindings were not created by this scripting context
      NoSuchMethodException - the function does not exist or the arguments do not match
    • callScriptMethod

      public final Object callScriptMethod(Object scriptObject, String methodName, Object... args) throws ScriptException, UnsupportedOperationException, NoSuchMethodException
      Evaluate a script object's method.
      Parameters:
      scriptObject - the script object whose method is to be invoked
      methodName - the name of the method to invoke
      args - the arguments
      Returns:
      the value computed by the method
      Throws:
      ScriptException - an exception occurred in a script object's method
      NoSuchMethodException - the method does not exist or the arguments do not match
      UnsupportedOperationException - scripting is not supported
      IllegalArgumentException - the scriptObject was null or is not an object recognized by the scripting language
    • callScriptMethod

      public final Object callScriptMethod(Bindings bindings, Object scriptObject, String methodName, Object... args) throws ScriptException, UnsupportedOperationException, NoSuchMethodException
      Evaluate a script object's method using specified bindings.
      Parameters:
      bindings - the bindings
      scriptObject - the script object whose method is to be invoked
      methodName - the name of the method to invoke
      args - the arguments
      Returns:
      the value computed by the method
      Throws:
      ScriptException - an exception occurred in a script object's method
      UnsupportedOperationException - scripting is not supported
      NoSuchMethodException - the method does not exist or the arguments do not match
    • containsScriptObject

      public final boolean containsScriptObject(String name)
      Determine if a script object with a given name exists (i.e., is contained the default bindings). The look-up uses the default bindings that were provided by the method getDefaultBindings() when it was called by a constructor. These bindings are the engine-scope bindings that will be used when the script is evaluated using the methods provided in this class.
      Parameters:
      name - the name of the object
      Returns:
      true if a binding for the name exists; false otherwise
    • getScriptObject

      public final Object getScriptObject(String name)
      Get a script object given a name. The object is obtained using the default bindings that were provided by the method getDefaultBindings() when it was called by a constructor. These bindings are the engine-scope bindings that will be used when the script is evaluated using the methods provided in this class.
      Parameters:
      name - the name of the object
      Returns:
      the object; null if there is none
    • putScriptObject

      public final void putScriptObject(String name, Object object) throws UnsupportedOperationException
      Assign a name to a script or java object. The object is stored in the default bindings that were provided by the method getDefaultBindings() when it was called by a constructor. These bindings are the engine-scope bindings that will be used when the script is evaluated using the methods provided in this class.
      Parameters:
      name - the name to assign to the object
      object - the object
      Throws:
      UnsupportedOperationException - the script context does not provide any bindings
    • doGetScriptEngine

      protected ScriptEngine doGetScriptEngine()
      Get the script engine. Subclasses providing a scripting environment must override this method. It will typically be called once, but could be called multiple times, so the method must be idempotent in case it is called multiple times. It will not be called by this class if the constructor provided a parent.
      Returns:
      the script engine for this scripting context; null if there is none
    • hasScriptEngine

      public final boolean hasScriptEngine()
      Determine if a script engine is available.
      Returns:
      true if there is a script engine; false otherwise
    • createBindings

      public final Bindings createBindings()
      Create a new set of script bindings.
      Returns:
      a new set of script bindings; null if there is no script engine
    • doGetDefaultBindings

      protected Bindings doGetDefaultBindings()
      Get the default script bindings. Subclasses providing a scripting environment must override this method. It will typically be called once, but could be called multiple times, so the method must be idempotent. It will not be called by this class if the constructor provided a parent.
      Returns:
      the default bindings for this scripting context; null if there are none
    • doGetScriptLanguage

      protected String doGetScriptLanguage()
      Get the scripting language. Subclasses providing a scripting environment must override this method. It will typically be called once, but could be called multiple times, so the method must be idempotent. It will not be called by this class if the constructor provided a parent.
      Returns:
      the script engine for this scripting context; null if there is none
    • getScriptLanguage

      public final String getScriptLanguage()
      Get the script language name.
      Returns:
      the name of the scripting language; null if there is none
    • setWriter

      public final void setWriter(Writer writer) throws UnsupportedOperationException
      Set the writer for script output.
      Parameters:
      writer - the writer
      Throws:
      UnsupportedOperationException - scripting was not enabled
    • getWriter

      public final Writer getWriter() throws UnsupportedOperationException
      Get the writer for scripting-language output.
      Returns:
      the writer
      Throws:
      UnsupportedOperationException - scripting was not enabled
    • setErrorWriter

      public final void setErrorWriter(Writer writer) throws UnsupportedOperationException
      Set the writer for script error output.
      Parameters:
      writer - the writer
      Throws:
      UnsupportedOperationException - scripting was not enabled
    • getErrorWriter

      public final Writer getErrorWriter() throws UnsupportedOperationException
      Get the write for scripting-language error output.
      Returns:
      the writer
      Throws:
      UnsupportedOperationException - scripting was not enabled
    • setReader

      public final void setReader(Reader reader) throws UnsupportedOperationException
      Set the reader for script input.
      Parameters:
      reader - the reader
      Throws:
      UnsupportedOperationException - scripting was not enabled
    • getReader

      public final Reader getReader() throws UnsupportedOperationException
      Get the reader for scripting-language input.
      Returns:
      the reader
      Throws:
      UnsupportedOperationException - scripting was not enabled
    • doScriptPrivileged

      protected final void doScriptPrivileged(ExceptionedCallable c) throws Exception
      Execute code, that can throw checked exceptions, in privileged mode.

      If a subclass overrides this method, it must call super.doScriptPrivleged with the same arguments. This may be necessary in order to make the method accessible to other classes in the subclass' package. Ideally, the method would be declared to be final, but this unfortunately precludes allowing protected access in the subclass' package.

      Parameters:
      c - an ExceptionedCallable providing the code to run
      Throws:
      Exception - an exception was raised
    • invokePrivateFunction

      protected final Object invokePrivateFunction(Bindings bindings, Properties properties, String functionName, Object... args) throws ScriptException, UnsupportedOperationException
      Invoke a function defined by a private script with specified bindings. The script is stored in a Properties list of properties under a key with the same name that getScriptLanguage() returns in order to allow multiple scripting languages to be supported (EMCAScript, the official name for Javascript, is supported by the JDK, with other scripting languages available separately). The script will be evaluated once in sandbox mode, and must return an object whose methods will represent the functions that can be called (while at the implementation level, the methods of an object are called, the object is not visible to the caller). The Invocable interface will then be used to evaluate the appropriate method without having to evaluate the script each time this method is called. A separate object is used for each bindings/properties pair.

      The evaluation of the function is performed in the mode specified by the third argument.

      Note: this method was added (with a change in the format of the properties for each language) because testing indicated that the Graal Javascript implementation, which a time this method was introduced, is likely to be the replacement for Nashorn, does not allow a function defined for one set of bindings to be used with objects defined in another set of bindings. To be sure this method would work with Graal's scripting languages, the object containing the methods is now placed in the same bindings used to obtain the arguments listed in the arguments that follow the function name.

      Parameters:
      bindings - the bindings to use when evaluating the function
      properties - a properties list containing the script as an entry for a given scripting-language name (ECMAScript for Javascript)
      functionName - the name of the function to invoke
      args - the function arguments
      Returns:
      the object produced by the script evaluation
      Throws:
      ScriptException - an error occurred during the execution of the script
      UnsupportedOperationException - no script engine was provided
    • invokePrivateFunction

      protected final Object invokePrivateFunction(Properties properties, String functionName, Object... args) throws ScriptException, UnsupportedOperationException
      Invoke a function from a private script. The script is stored in a Properties list of properties under a key with the same name that getScriptLanguage() returns in order to allow multiple scripting languages to be supported (EMCAScript, the official name for Javascript, is supported by the JDK, with other scripting languages available separately). The script will be evaluated once in sandbox mode, and must return an object whose methods will represent the functions that can be called (while at the implementation level, the methods of an object are called, the object is not visible to the caller). The Invocable interface will then be used to evaluate the appropriate method without having to evaluate the script each time this method is called. A separate object is used for each bindings/properties pair, and the bindings are those currently in effect for this scripting context's script engine.

      The evaluation of the function is performed in the mode specified by the second argument.

      Note: this method was added (with a change in the format of the properties for each language) because testing indicated that the Graal Javascript implementation, which a time this method was introduced, is likely to be the replacement for Nashorn, does not allow a function defined for one set of bindings to be used with objects defined in another set of bindings. To be sure this method would work with Graal's scripting languages, the object containing the methods is now placed in the same bindings used to obtain the arguments listed in the arguments that follow the function name.

      Parameters:
      properties - a properties list containing the script as an entry for a given scripting-language name (ECMAScript for Javascript)
      functionName - the name of the function to invoke
      args - the function arguments
      Returns:
      the object produced by the script evaluation
      Throws:
      ScriptException - an error occurred during the execution of the script
      UnsupportedOperationException - no script engine was provided
    • getNames

      public Set<String> getNames()
      Get a set of object names. These names are the names for scripting-language objects that are defined in the current engine-scope and global-scope bindings. This method is useful mainly for debugging or for obtaining data for documentation.
      Returns:
      an unmodifiable set of the current object names