Script Module

Last modified by Thomas Mortagne on 2024/07/05 14:31

cogScripting APIs, on top of JSR-223
TypeJAR
Category
Developed by

XWiki Development Team

Rating
0 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Standard

Compatibility

XWiki > 2.3

Description

XWiki's scripting features are built on top of JSR-223.

The Script Module offers the following:

  • Defines ScriptEvaluatedEvent and ScriptEvaluatingEvent events (see Observation Module) for being called before a script macro executes or just after it's been executed. The ScriptEvaluatedEvent  event even allow to cancel the execution of the script macro.
  • Provides a ScriptService component role allowing to expose APIs to script macros (see below)
  • Allow setting and accessing script binding trough ScriptContext

Setting script binding

@Inject
private ScriptContextManager manager;

public void setMyVarForNextScript()
{
  
// Best practice since 8.3 to access the current ScriptContext
  
ScriptContext scriptContext = this.manager.getCurrentScriptContext();
  
// #getCurrentScriptContext does not exist before 8.3
  
// ScriptContext scriptContext = this.manager.getScriptContext();

  
scriptContext.setAttribute("myvar", "my value", ScriptContext.ENGINE_SCOPE);
}

Any script executed after this will be able to use myvar variable directly. This is true also for Velocity since 8.3.

Script Services

To expose an API to a script macro you simply need to implement the org.xwiki.script.service.ScriptService component role (see Component Module to know more about components). Note that it is rather usual to not separate script-services between interface and implementation as the visibility is at a different level than that of java. Rather, script-service components should perform mostly delegation to other components or simple transformations (such as objects translation).

For example:

@Component
@Named("my")
@Singleton
public class MyScriptService implements ScriptService
{
   // Declare all java methods you wish to expose here. For example:
   public void doSomething(String whatever)
    {
        ...
    }
}

Of course, make sure to register your component in components.txt and to deploy your component JAR in WEB-INF/lib.

Then to use this in a Velocity script macro for example you'll write:

{{velocity}}
$services.my.doSomething("something here")
{{/velocity}}

Script Safe providers

XWiki 16.2.0+

A component org.xwiki.script.safe.ScriptSafeProvider can be injected (without any generic type) to get a version of an object which is safe (especially in the case of Velocity scripts).

To introduce new custom provider for your own types, you can implement a component with role org.xwiki.script.safe.ScriptSafeProvider<MyType> where MyType is the type of the object which is made safe.

Best Practices

Exceptions

Since the Velocity try() directive has been introduced the new best practice is:

  • Script Services should throw exceptions (instead of try/catching them and returning null and setting an xcontext property as before)
  • If Velocity scripts (or any script) wish to handle the exceptions, they should use the #try() directive. If they don’t want to, they don’t have to do anything since the Macro Transformation or the template (contentvars.vm for example) will catch it and display it to the user.
  • Existing Script APIs in Java should not be modified as that would break backward compatibility. New signatures can be added and old one deprecated and moved to the legacy modules. After new signatures have been introduced, existing Velocity scripts can be updated to use the new signatures and to use the #try directive if needed.

Tutorials

Learn how to write a script service:

Get Connected