Sling Scripting is build around Java Scripting API (JSR 223). It allows the easy development and usage of different scripting (aka templating) engines.
The script engines are managed in SlingScriptEngineManager
(Scripting Core).
Engine | Language Name | Language Version | Names | Extensions | Mime Types | GitHub Repo(s) | Documentation |
---|---|---|---|---|---|---|---|
FreeMarker | FreeMarker | freemarker.template.Configuration#getVersion().toString() | FreeMarker freemarker (configurable) | ftl (configurable) | text/x-freemarker (configurable) | sling-org-apache-sling-scripting-freemarker | |
Groovy (GString) | Groovy GString | org.codehaus.groovy.util.ReleaseInfo#getVersion() | GString gstring (configurable) | gst (configurable) | (configurable) | sling-org-apache-sling-scripting-groovy | |
HTL | The HTL Templating Language | 1.4 | htl HTL sightly | html | sling-org-apache-sling-scripting-sightly | Scripting HTL | |
Java | Java Servlet Compiler | 1.5 | java Java | java | sling-org-apache-sling-scripting-java | ||
JavaScript | ECMAScript | partial ECMAScript 2015 support | rhino Rhino javascript JavaScript ecmascript ECMAScript | esp ecma | text/ecmascript text/javascript application/ecmascript application/javascript | sling-org-apache-sling-scripting-javascript | |
JSP | Java Server Pages | 2.1 | jsp JSP | jsp jspf jspx | sling-org-apache-sling-scripting-jsp | Scripting JSP | |
Thymeleaf | Thymeleaf | version from /org/thymeleaf/thymeleaf.properties | Thymeleaf thymeleaf (configurable) | html (configurable) | text/html (configurable) | sling-org-apache-sling-scripting-thymeleaf | Scripting Thymeleaf |
Several more engines are available but experimental or no longer maintained:
Code for really old modules might be found in the svn attic.
Since version 2.0.60
Scripting Core supports the mapping of extensions to engines in content (SLING-4330).
This is required when registering more than one script engine for a single script extension (e.g. using HTL for vendor-related scripts in /libs
and Thymeleaf for project-related scripts in /apps
, both using extension html
).
It works by adding a sling:scripting
property to the script resource or a resource in the hierarchy above the script (e.g. project or parent folder).
The mapping consists of a key which is the script extension and a value which itself could consist of four values separated by colon to identify the script engine:
The sling:scripting
property supports multiple mappings for different extensions (e.g. html
and js
).
In case there is more than one script engine for a script extension registered and no mapping is found the script engine with higher service ranking gets used for rendering.
Mapping html
to HTL 1.4:
"sling:scripting": [
"html=The HTL Templating Language:1.4"
]
Mapping html
to Thymeleaf 3.0:
"sling:scripting": [
"html=Thymeleaf:3.0"
]
The Sling API defines a SlingScript
interface which is used to represent (executable) scripts inside of Sling. This interface is implemented in the Scripting Core bundle in the DefaultSlingScript
class which also implements the javax.servlet.Servlet
.
To further simplify the access to scripts from the Resource tree, the scripting/core
bundle registers an AdapterFactory
to adapt Resources to Scripts and Servlets (the SlingScriptAdapterFactory
). In fact the adapter factory returns instances of the DefaultSlingScript
class for both Scripts and Servlets.
From the perspective of the Servlet resolver, scripts and servlets are handled exactly the same. In fact, internally, Sling only deals with Servlets, whereas scripts are packed inside a Servlet wrapping and representing the script.
Scripts are looked up in a series of resource resolver locations defined by the ResourceResolver.getSearchPath()
and the resource type (and resource super types) of the requested resource:
{scriptPathPrefix}/{resourceTypePath}
The pseudo code for iterating the locations would be something like:
var type = resource.getResourceType();
while (type != null) {
for (String root: resourceResolver.getSearchPath()) {
String path = root + type.toPath();
findScriptsIn(path);
}
if (type == defaultServlet) {
type = null;
} else {
type = getResourceSuperType(type);
if (type == null) {
type = defaultServlet;
}
}
}
All resource providers may contribute scripts (this includes particularly the JcrResourceProvider
but also BundleResourceProvider
)
Depending on whether request selectors are considered, a script may have two forms:
{resourceTypeLabel}.{requestExtension}.{requestMethod}.{scriptExtension}
{selectorStringPath}.{requestExtension}.{requestMethod}.{scriptExtension}
The constituents of these script names are as follows:
{resourceTypeLabel}
- The last path segment of the path created from the resource type. This part is optional if the {requestExtension}
is used in the script name. The resource type might either be set via the sling:resourceType
property on the accessed node or if that property is not there its primary node type (property jcr:primaryType
) is taken as fall-back.{requestExtension}
- The request extension. This part may be omitted if the request extension is html
, otherwise this part is required. If this part is omitted, the {resourceTypeLabel}
is required in the case of ignoring the selectors.{requestMethod}
- The request's HTTP method. This part may be omitted if the script is meant for a GET
or a HEAD
request. This part is required for any other HTTP method.{scriptExtension}
- The extension identifying the scripting language used. This part is mandatory. For more details about the available Script Engines and their registered extensions check the Sling Scripting page.{selectorStringPath}
- The selector string converted to a path, along the lines of selectorString.replace('.', '/')
. If less selectors are specified in the script name than given in the request, the script will only be taken into consideration if the given selectors are the first selectors in the request. This means sel1/sel2.html.jsp will be a candidate for the request url /content/test.sel1.sel2.sel3.html but not for /content/test.sel3.sel1.sel2.html. So the order of selectors is relevant!Next to using Bundle Resources which allow to place resource scripts in bundles, scripts may also be provided in OSGi bundles directly (precompiled or embedded) since Sling Servlet Resolver 2.7.0 through the org.apache.sling.servlets.resolver.bundle.tracker
API.
Although traditionally scripts are deployed as content stored in the search paths of a Sling instance, this leaves very little room for script evolution in a backwards compatible way. Furthermore, versioning scripts is a difficult process if the only mechanism to do this is the sling:resourceType
property, since consumers (content nodes or other resource types) have then to explicitly mention the version expected to be executed.
Scripts should not be considered content, since their only purpose is to actually generate the rendering for a certain content structure. They are not consumed by users, but rather by the Sling Engine itself and have very little meaning outside this context. As such, scripts should be handled like code:
Being built around a BundleTrackerCustomizer
, the org.apache.sling.servlets.resolver.internal.bundle.BundledScriptTracker
monitors the instance's bundles wired to the org.apache.sling.servlets.resolver
bundle and scans the ones providing a sling.servlet
capability. The wiring is created by placing a Require-Capability
header in the bundles that provide the sling.servlet
capability:
osgi.extender;filter:="(&(osgi.extender=sling.scripting)(version>=1.0.0)(!(version>=2.0.0)))"
A sling.servlet
capability has almost the same attributes as the properties required to register a servlet on the Sling platform:
sling.servlet.resourceTypes:List
- mandatory; defines the provided resource type; its value is a list of resource typessling.servlet.selectors:List
- optional; defines the list of selectors that this resource type can handle;sling.servlet.extensions:List
- optional; defines the list of extensions that this resource type can handle;sling.servlet.methods:List
- optional; defines the list of HTTP methods that this resource type can handle;version:Version
- optional; defines the version of the provided resourceType
;extends:String
- optional; defines which resource type it extends; the version range of the extended resource type is defined in a Require-Capability
.The BundledScriptTracker
will register a Sling Servlet with the appropriate properties for each sling.servlet
capability. The servlets will be registered using the bundle context of the bundle providing the sling.servlet
capability, making sure to expose the different versions of a resource type as part of the registered servlet's properties. On top of this, a plain resource type bound servlet will also be registered, which will be automatically wired to the highest version of the resourceType
. All the mentioned service registrations are managed automatically by the BundledScriptTracker
.
Short answer: exactly like you deploy your code, preferably right next to it. Pack your scripts using the following conventions:
src/main/resources/javax.script
folder in your bundle (if you want to embed the scripts as they are) or just put the scripts in src/main/scripts
if you want to precompiled them (e.g. JSP and HTL);resourceType
;resourceType
folder you can optionally create a Version
folder; this has to follow the Semantic Versioning constraints described at 1;scriptingbundle-maven-plugin
to your build section and add its required properties in the maven-bundle-plugin
's instructions (check these examples);mvn clean sling:install
.The integration tests for bundled scripts are provided by the org.apache.sling.scripting.bundle.tracker.it
project.
The same rules as for servlets are being followed but in addition keep in mind that bundled scripts (as well as servlets) are prefered over resource scripts (in case there are multiple equally ranked candidates left after all other criteria).
Let's consider the following script paths for a request of a resource whose resource type is sling\sample
and the request selectors are print.a4 and the request extension is html:
The priority of script selection would be (starting with the best one):
(7) - (5) - (6) - (4) - (3) - (2) - (1).
Note that (5) is a better match than (6) because it matches more selectors even though (6) has an extension match where (5) does not. (8) is not a candidate because it does not include the first selector (print) and (9) is not a candidate because it has the wrong order of selectors.
All scripts backed by Sling resources get their character encoding from the character encoding set in the resource metadata. For JCR based resources this is retrieved from the underlying jcr:encoding
JCR property. If not set it will fall back to UTF-8.
Every script evaluation in the context of a request sets the response's character encoding to UTF-8 (if the request accepts content types starting with text/
)
See also Scripting variables and Adding New Scripting Variables.