The latest version of this document can be found here.

If you have comments, remarks or questions about this manual, please use the hypothes.is panel on the right side of this page. Use this invite link to get access to the XSLWeb group. To add annotations to selected text, please use your right mouse button.

1. The fundamentals of XSLWeb

1.1. Introduction

XSLWeb is an open source and free to use web development framework for XSLT and XQuery developers. It is based on concepts similar to frameworks like Cocoon and Servlex, but aims to be more easily accessible and pragmatic.

Using XSLWeb, XSLT/XQuery developers can develop both web applications (dynamic websites) and web services. In essence, an XSLWeb web application is one or more XSLT stylesheets (version 1.0, 2.0 or 3.0) or XQueries (version 1.0, 3.0 or 3.1) that transform an XML representation of the HTTP request (the Request XML) to an XML representation of the HTTP response (the Response XML). Which specific XSLT stylesheet or XQuery (or pipeline of XSLT stylesheets and XQueries) must be executed for a particular HTTP request is governed by another XSLT stylesheet, the request dispatcher stylesheet.

After every XSLT transformation step, an optional validation pipeline step (XML Schema or Schematron) can be added to validate the result of the previous transformation step.

During transformations, data sources can be accessed using a library of built-in extension functions that provide HTTP communication (for example to consume REST or SOAP based web services), file and directory access, relational database access and so on.

The result of a transformation pipeline can be serialized to XML, (X)HTML or plain text format and using specific serializer pipeline steps to JSON, ZIP files, PDF, Postscript or RTF (using XSL:FO and Apache FOP).

The configuration of an XSLWeb web application can be specified in an XML configuration document called webapp.xml. An XSLWeb server can contain multiple separate web applications.

Diagram 1 shows the flow of a HTTP request to a HTTP response within XSLWeb:

XSLWeb request and response flow

XSLWeb flow

  1. A HTTP request is sent from a client (a web browser or webservice client).

  2. The HTTP request is serialized by the Request Serializer to a Request XML document. All information of the request is preserved in the XML.

  3. The Request XML is the input of the Request Dispatcher, which transform the Request XML using the webapp specific XSLT stylesheet request-dispatcher.xsl. The output of this transformation is a pipeline specification, in the simplest form only specifying the path to a XSLT stylesheet that will be used to transforming the Request XML to the Response XML. This specification could also contain a pipeline of multiple XSLT transformations and XML Schema or Schematron validations.

  4. The pipeline specification is the input for the Pipeline Processor, which reads the Pipeline XML and executes the pipeline transformation and validation steps. The input for the first transformation in the pipeline is the same Request XML as was used as input for the Request Dispatcher.

  5. The Pipeline Processor executes your pipeline of XSLT stylesheets, XQueries and validations. The last transformation in the pipeline must generate a Response XML document which conforms to the schema «xslweb-home»/config/xsd/xslweb/response.xsd.

  6. The Response XML is then passed on to the Response Deserializer, which interprets your Response XML and converts it to a HTTP response, which is sent back to the client, a web browser of webservice client (7).

1.2. The Request XML

The Request XML is an XML representation (or XML serialization) of the HTTP Request. The Request XML conforms to the XML Schema «xslweb-home»/config/xsd/xslweb/request.xsd, and contains the following information:

  • The request properties: auth-type, character-encoding, content-length, context-path, content-type, local-addr, local-name, local-port, method, path, path-info, path-translated, protocol, query-string, remote-addr, remote-host, remote-port, remote-user, requested-session-id, request-URI, request-url, scheme, server-name, server-port, servlet-path, webapp-path, is-secure, is-requested-session-id-from-cookie, is-requested-session-id-from-url and is-requested-session-id-valid.

  • HTTP headers

  • Request parameters

  • Request body

  • File uploads

  • Session information

  • Cookies

1.3. The Response XML

The Response XML is a XML representation (or XML serialization) of the HTTP Response. The Response XML must conform to the XML Schema «xslweb-home»/config/xsd/xslweb/response.xsd, and contains the following information:

  • HTTP headers

  • Response body

  • Session information

  • Cookies

1.4. The Request dispatcher XSLT stylesheet

The task of the XSLT stylesheet request-dispatcher.xsl is to dynamically generate the pipeline specification that is then used to process the Request XML and convert it to the Response XML. The input of the request dispatcher transformation is the Request XML so it has all information available to generate the correct pipeline. The output of the request dispatcher transformation is a pipeline specification that must conform to the XML Schema «xslweb-home»/config/xsd/xslweb/pipeline.xsd.

Below is an example of a very basic request dispatcher stylesheet that generates a valid pipeline for the HTTP request http://my-domain/my-webapp/hello-world.html:

Basic request dispatcher stylesheet
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:req="http://www.armatiek.com/xslweb/request"
  xmlns:pipeline="http://www.armatiek.com/xslweb/pipeline"
  version="3.0">

  <xsl:template match="/req:request[req:path = '/hello-world.html']">
    <pipeline:pipeline>
      <pipeline:transformer
        name="hello-world"
        xsl-path="hello-world.xsl"
        log="true"/>
    </pipeline:pipeline>
  </xsl:template>

</xsl:stylesheet>

The following example uses the request parameter lang in the request http://my-domain/my-webapp/hello-world.html?lang=en to determine the stylesheet. This lang parameter is also passed to the stylesheet as a stylesheet parameter:

Extended request dispatcher stylesheet
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:req="http://www.armatiek.com/xslweb/request"
  xmlns:pipeline="http://www.armatiek.com/xslweb/pipeline"
  version="3.0">

  <xsl:template match="/req:request[req:path = '/hello-world.html']">
    <xsl:variable
      name="lang"
      select="req:parameters/req:parameter[@name='lang']/req:value[1]"/>
    <pipeline:pipeline>
      <pipeline:transformer
        name="hello-world"
        xsl-path="{concat('hello-world-', $lang, '.xsl')}">
        <pipeline:parameter
          name="lang"
          uri="http://www.armatiek.com/xslweb/my-namespace"
          type="xs:string">
          <pipeline:value>
            <xsl:value-of select="$lang"/>
          </pipeline:value>
        </pipeline:parameter>
      </pipeline:transformer>
    </pipeline:pipeline>
  </xsl:template>

</xsl:stylesheet>

A pipeline consists of:

  • One or more of the following transformation pipeline steps:

    • transformer: transforms the input of the pipeline step using an XSLT version 1.0, 2.0 or 3.0 stylesheet.

    • query: processes the input of the pipeline step using an XQuery version 1.0, 3.0 or 3.1 query.

    • transformer-stx: transform the input of the pipeline step using a STX (Streaming Transformations for XML) version 1.0 stylesheet.

  • Zero or more of the following validation pipeline steps:

    • schema-validator: validates the input of the step using an XML Schema version 1.0.

    • schematron-validator: validates the input of the step using an ISO Schematron schema.

  • Zero or one of the following serialization pipeline step

    • json-serializer: serializes XML output to a JSON representation.

    • zip-serializer: serializes a XML ZIP specification to an actual ZIP file.

    • resource-serializer: serializes a text or binary file to the response.

    • binary-serializer: serializes xs:base64Binary data as binary file to the response.

    • fop-serializer: serializes XSL-FO generated in a previous pipeline step to PDF using the Apache FOP XSL-FO processor.

See Pipeline steps for a more in depth overview of these different pipeline steps.

In development-mode, the output of the (intermediate) transformation steps can be logged to a log file, see chapters Development mode and production mode and Logging.

1.5. The pipeline transformation stylesheets

The result of the request dispatcher stylesheet is a pipeline specification containing one or more transformation, query, validation or serialization steps. The input of the first stylesheet or query in the pipeline is the Request XML, the output of the last stylesheet in the pipeline must conform to the Response XML schema.

XSLWeb extends the standard XSLT/XPath 1.0, 2.0 and 3.0 functionality in a number of ways:

  • XSLWeb provides a number of built-in XPath extension functions that you can use to read and write files and directories, execute HTTP requests, access the Request, Response and Context, Session and WebApp objects, log messages, send e-mails and so on, see XPath extension function library.

  • Other pipelines can be called from within a stylesheet and the result of this nested pipeline can be used or embedded in the calling stylesheet by passing a URI that starts with the scheme “xslweb://” to the standard XSLT document() function, see Nested pipelines.

  • URLs that are passed to XSLT’s document() or XQuery’s doc() function and must be proxied through a proxy server can be provided with two extra request parameters: proxyHost and proxyPort.

  • Within every transformation a number of standard stylesheet parameters is available, see Stylesheet parameters.

1.6. Web applications

An XSLWeb installation can contain multiple separate web applications. A web application can be added under the folder «xslweb-home»/webapps and has the following minimal folder structure (bold):

  • my-webapp/

    • lib/

    • static/

    • xsl/

      • request-dispatcher.xsl

      • my-stylesheet.xsl

    • xquery/

      • my-query.xq

    • stx/

      • my-stylesheet.stx

    • xsd/

      • my-xml-schema.xsd

    • sch/

      • my-schematron.sch

    • webapp.xml

This web application can be accessed by using the following uri:

If you use the jar distribution of XSLWeb with default parameters, this uri will be:

Out of the box, XSLWeb contains four web applications, “documentation”, “examples”, “my-webapp” and “ROOT”. The ROOT web application is used for Uri’s that doesn’t contain a reference to a web application, for instance:

The folder my-webapp can have any name you like (provided it doesn’t contain spaces or other strange characters). The folder lib can contain any custom XPath extension functions you have developed in Java and 3rd party libraries they depend on, see Custom extension functions. The folder static contains all static files you use in your web application, like images, css stylesheets and javascript files. The folder xsl contains the XSLT stylesheet request-dispatcher.xsl and at least one pipeline XSLT stylesheet that transforms Request XML to Response XML. The folders xsd and sch can contain XML Schema or Schematron validation specifications. The file webapp.xml contains further configuration of your web application.

The file webapp.xml contains the configuration of your web application. It must conform to the XML Schema «xslweb-home»/config/xsd/xslweb/xslweb-webapp.xsd, and contains the following configuration items:

  • Title: The title of your web application

  • Description: The description of your web application

  • Development-mode: see Development mode and production mode.

  • Resources: The definition of requests to static files that should not be processed by the request dispatcher (but should be served straight away) and the duration these resources should be cached by the browser (default 4 hours).

  • Parameters: The definition of webapp specific configuration parameters that are passed as stylesheet parameters to every XSLT transformation, see Stylesheet parameters.

  • Jobs: The definition of scheduled jobs, see Job scheduling.

  • Data sources: the definition of JDBC data sources.

  • FOP configurations: configurations for the Apache FOP serialization step, see fop-serializer step.

See Appendix C: Webapp XML example for an example of a webapp.xml configuration.

2. Pipeline steps

2.1. Transformation pipeline steps

The following transformation pipeline steps are available:

  • transformer

  • query

  • transformer-stx

2.1.1. transformer step

The transformer step transforms the input of the pipeline step using an XSLT version 1.0, 2.0 or 3.0 stylesheet.

Example:

Transformer pipeline step
<pipeline:pipeline>
  <pipeline:transformer name="my-xsl-step" xsl-path="my-stylesheet.xsl"/>
</pipeline:pipeline>

2.1.2. query step

The query step processes the input of the pipeline step using an XQuery version 1.0, 3.0 or 3.1 query.

Example:

Query pipeline step
<pipeline:pipeline>
  <pipeline:query name="my-xquery-step" xquery-path="my-query.xq"/>
</pipeline:pipeline>

See also example 27 of the examples webapp

2.1.3. transformer-stx step

The transformer-stx step transforms the input of the pipeline step using a STX (Streaming Transformations for XML) version 1.0 stylesheet.

Example:

STX pipeline step
<pipeline:pipeline>
  <pipeline:transformer-stx name="my-stx-step" stx-path="my-stylesheet.stx"/>
</pipeline:pipeline>

See also example 28 of the examples webapp

These steps could be combined in a pipeline as follows:

Multiple pipeline steps
<pipeline:pipeline>
  <pipeline:transformer-stx name="my-stx-step" stx-path="my-stylesheet.stx"/>
  <pipeline:transformer name="my-xsl-step" xsl-path="my-stylesheet.xsl"/>
  <pipeline:query name="my-xquery-step" xquery-path="my-query.xq"/>
</pipeline:pipeline>

2.1.4. Extensions

XSLWeb extends the standard functionality of the transformation steps in a number of ways:

  • XSLWeb provides a number of built-in XPath extension functions to the transformer and query steps (not the transformer-stx step) that you can use to read and write files and directories, execute HTTP requests, access the Request, Response and Context, Session and WebApp objects, log messages, send e-mails and so on, see XPath extension function library.

  • Other pipelines can be called from within a stylesheet or query and the result of this nested pipeline can be used or embedded in the calling stylesheet/query by passing a URI that starts with the scheme “xslweb://” to the standard XSLT/XQuery document() function or the STX stx:process-children element, see Nested pipelines.

  • Within every transformation of query a number of standard stylesheet parameters is available, see Stylesheet parameters.

2.2. Validation pipeline steps

XSLWeb supports the XML validation of the output of a transformation pipeline step by adding a validation pipeline step after the transformation step.

The following validation pipeline steps are available:

  • schema-validator

  • schematron-validator

2.2.1. schema-validator step

The schema-validator step validates the output of the previous step using an XML Schema version 1.0.

Schema validator pipeline step
<pipeline:pipeline>
  <pipeline:transformer name="my-transformation " xsl-path="my-transormation.xsl"/>
  <pipeline:schema-validator
    name="schema-validator"
    xsl-param-namespace="http://www.armatiek.com/xslweb/validation"
    xsl-param-name="schema-validation-report">
    <pipeline:schema-paths>
      <pipeline:schema-path>my-schema.xsd</pipeline:schema-path>
    </pipeline:schema-paths>
  </pipeline:schema-validator>
</pipeline:pipeline>

The location(s) of the XML schemas can be specified in the subelements schema-path. These paths must be relative to the directory «webapp»/xsd.

Any validation warnings and errors are written to the log file. If you specify the attribute xsl-param-name (and optional attribute xsl-param-namespace), a validation report (in XML format) is added as a stylesheet parameter of type document-node() to the next XSLT transformation step in the pipeline.

Validation properties (like http://javax.xml.XMLConstants/property/accessExternalSchema) and features (like http://javax.xml.XMLConstants/feature/secure-processing) can be specified in the features and properties subelements (see pipeline.xsd).

See also example 25 of the examples webapp.

2.2.2. schematron-validator step

The Schematron-validator step validates the output of the previous step using an ISO Schematron schema:

Schematron validator pipeline step
<pipeline:pipeline>
  <pipeline:transformer name="my-transformation " xsl-path="my-transormation.xsl"/>
  <pipeline:schematron-validator
    name="schematron-validator"
    schematron-path="my-schematron.sch "
    xsl-param-namespace="http://www.armatiek.com/xslweb/validation"
    xsl-param-name="schematron-validation-report">
  </pipeline:schematron-validator>
</pipeline:pipeline>

The location of the Schematron schema can be specified in the attribute schematron-path. This path must be relative to the directory «xslweb-home»/sch.

Any validation warnings and errors are written to the log file. If you specify the attribute xsl-param-name (and optional attribute xsl-param-namespace), the validation report (in SVRL format) is added as a stylesheet parameter of type document-node() to the next XSLT transformation step in the pipeline.

The Schematron phase can be specified using the optional attribute phase on the element schematron-validator (see pipeline.xsd).

See also example 25 of the examples webapp.

2.3. Serialization pipeline steps

The way the result of the transformation pipeline steps is serialized to XML, XHTML, HTML or text can be specified by the serialization attributes of the element xsl:output in the last stylesheet or query of the pipeline, using the attributes method, encoding, indent, omit-xml-declaration and so on.

In case the output of the pipeline should not be XML, XHTML, HTML or text, a specific serialization pipeline step can be added at the end of the pipeline. XSLWeb provides the serialization steps:

  • json-serializer

  • zip-serializer

  • resource-serializer

  • binary-serializer

  • fop-serializer

2.3.1. json-serializer step

The json-serializer step serializes XML to a JSON representation. This step can be added as the last step in a pipeline like this:

JSON serializer pipeline step
<pipeline:pipeline>
  <pipeline:transformer
    name="my-transformation"
    xsl-path="my-transformation.xsl"/>
  <pipeline:json-serializer
    name="json-serialization"
    auto-array="false"
    pretty-print="true">
  <pipeline:json-serializer/>
</pipeline:pipeline>

The pipeline step supports the following attributes:

  • auto-array

  • auto-primitive

  • multi-pi

  • namespace-declarations

  • namespace-separator

  • pretty-print

  • virtual-root-namespace

  • virtual-root-name

  • repairing-namespaces

See for an explanation of these properties the documentation of StAXON.

Namespace declarations can be specified by adding namespace-declaration elements under the namespace-declarations sub element of json-serializer (see pipeline.xsd).

See also example 18 of the examples webapp.

2.3.2. zip-serializer step

The zip-serializer step serializes an XML representation of the contents of a ZIP file to the actual file.

A ZIP serializer pipeline step can be added as the last step in a pipeline like this:

ZIP serializer pipeline step
<pipeline:pipeline>
  <pipeline:transformer
    name="my-zip-serialization"
    xsl-path="my-zip-serialization.xsl"/>
  <pipeline:zip-serializer name="zip"/>
</pipeline:pipeline>

The last transformation step in the pipeline has to generate a response like the following XML:

Specify ZIP contents
<resp:response status="200">
  <resp:body>
    <zip:zip-serializer
      xmlns:zip="http://www.armatiek.com/xslweb/zip-serializer">
      <zip:file-entry
        name="file/myfile.txt"
        src="/home/john/myfile.txt"/>
      <zip:inline-entry
        name="dir1/test.xml"
        method="xml"
        encoding="UTF-8"
        omit-xml-declaration="no"
        indent="yes">
        <a>
          <b>Hello World</b>
        </a>
      </zip:inline-entry>
    </zip:zip-serializer>
  </resp:body>
</resp:response>

The element zip-serializer can contain two elements:

  • zip:file-entry: a representation of a disk file that must be serialized to the zip file. The attribute “src” holds the path to the file, the attribute “name” holds the name (path) of the file in the serialized zip file.

  • zip:inline-entry: an inline XML, HTML or text fragment that must be serialized to the ZIP file. The attribute “name” holds the name (path) of the file in the serialized zip file. Other attributes specify the serialization behavior and are the same as the attributes of xsl:output.

See also example 23 of the examples webapp.

2.3.3. resource-serializer step

The resource serializer returns a stored text or binary file to the client. It is typically used in scenarios where a file is dynamically generated during pipeline execution (for instance by using the extension function exec-external()) and then must be returned to the client in the same request. Another scenario is to create download links to static files that are located on the server outside the scope of the webapp’s “static” directory.

The resource serializer pipeline step can be added as the last step in a pipeline like this:

Resource serializer pipeline step
<pipeline:pipeline>
  <pipeline:transformer
    name="resource-serialization"
    xsl-path="resource-serialization.xsl"/>
  <pipeline:resource-serializer name="resource"/>
</pipeline:pipeline>

In this example the stylesheet resource-serialization.xsl must generate a XML fragment containing information that the resource serializer uses to create the desired response. The stylesheet must generate an element resource-serializer in the namespace http://www.armatiek.com/xslweb/resource-serializer. On this element the following attributes can be set:

  • path: the local path to the file that must be returned to the client.

  • content-type (optional): the content type (mime type) that must be set on the response. If this attribute is not set, XSLWeb will try to determine the content type automatically.

  • content-disposition-filename (optional): this attribute can be used to force the browser to display a “Save as” dialog (instead of display the file “inline”). The specified filename will be used as the default filename in de dialog.

  • expire-time (optional): The time the resource may be cached by the client before it expires (in seconds).

An example of the output of the XSLT stylesheet resource-serialization.xsl is:

Resource
<res:resource-serializer
  path="webapps/examples/xsl/resource/leaves.jpg"
  content-type="image/jpg"
  content-disposition-filename="my-image.jpg"
  expire-time="30"/>

See also example 26 of the examples webapp

2.3.4. binary-serializer step

The binary serializer returns xs:base64Binary data as a binary file to the client. It is typically used in scenarios where binary data is fetched from a HTTP server using EXPath HttpClient and this data must be returned to the client as a binary file.

The binary serializer pipeline step can be added as the last step in a pipeline like this:

Binary serializer pipeline step
<pipeline:pipeline>
  <pipeline:transformer
    name="binary-serialization"
    xsl-path="binary-serialization.xsl"/>
  <pipeline:binary-serializer name="binary"/>
</pipeline:pipeline>

In this example the stylesheet binary-serialization.xsl must generate a XML fragment containing information that the binary serializer uses to create the desired response. The stylesheet must generate an element binary-serializer in the namespace http://www.armatiek.com/xslweb/binary-serializer. The contents of this element must be an base64 encoded string.

An example of the output of the XSLT stylesheet binary-serialization.xsl is:

Binary
<res:binary-serializer>iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==</res:binary-serializer>

2.3.5. fop-serializer step

The fop-serializer step serializes XSL:FO format generated in a previous pipeline step to document formats like PDF or RTF using the Apache FOP processor version 2.2.

The FOP serializer pipeline step can be added as the last step in a pipeline like this:

FOP serializer pipeline step
<pipeline:pipeline>
  <pipeline:transformer
    name="my-xsl-fo-serialization"
    xsl-path="my-xsl-fo-serialization.xsl"/>
  <pipeline:fop-serializer name="fop-serialization"/>
</pipeline:pipeline>

The last transformation step in the pipeline has to generate a response like the following XML:

<resp:response status="200">
  <resp:body>
    <fop:fop-serializer
      xmlns:fop="http://www.armatiek.com/xslweb/fop-serializer"
      output-format="application/pdf"
      config-name="default">
      <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
        <!-- Your further XSL:FO code -->
      </fo:root>
    </fop:fop-serializer>
  </resp:body>
</resp:response>

The fop:fop-serializer element supports the following attributes:

  • config-name: the name of a FOP configuration in webapp.xml.

  • output-format (optional): the output format of the serialization, like “application/pdf” (default), application/postscript, application/rtf (see the FOP class MimeConstants.java).

  • pdf-a-mode (optional): specify a PDF/A profile:

    • PDF/A-1a

    • PDF/A-1b

    • PDF/A-2a

    • PDF/A-2b

    • PDF/A-2u

    • PDF/A-3a

    • PDF/A-3b

    • PDF/A-3u

See also example 24 of the examples webapp.

3. Stylesheet parameters

Every XSLT stylesheet that is executed within XSLWeb is provided with a number of stylesheet parameters:

  • The configuration parameters from the parameters section in the webapp.xml. The parameter’s local name can be given a namespace using the attribute uri and the type of the values can be specified using the attribute type. The value itself can be a sequence of atomic values.

  • config:home-dir (xs:string): the path to the XSLWeb home directory (config = http://www.armatiek.com/xslweb/configuration)

  • config:webapp-dir (xs:string): the path to the base directory of the webapp.

  • config:webapp-path (xs:string): The path in de url to the web application (“/” for the webapp ROOT and “/” + webapp-name for other webapps).

  • config:development-mode (xs:boolean): whether the webapp runs in development mode (true()) or production-mode (false()).

  • config:debug-mode (xs:boolean): whether the webapp runs in debug mode or not.

  • config:cache-buster-id (xs:string): see Cache busting.

  • req:request-xml-doc (document-node()): the request object, serialized to XML, which is also the input document of every pipeline (req = http://www.armatiek.com/xslweb/request)

  • The Java HttpServletRequest, HttpServletResponse and WebApp objects. These are used in custom XPath extension functions.

Pipeline stylesheets are also provided with any parameters that are defined within the element pipeline:transformer in request-dispatcher.xsl. The parameter’s local name can be given a namespace using the attribute uri and the type of the values can be specified using the attribute type. The value itself can be a sequence of atomic values.

The parameters only have to be declared in the stylesheets (as <xsl:param/> elements) when they are actually used. The parameters for the Java objects doesn’t have to be declared at all.

4. Nested pipelines

It is possible to call another pipeline from a stylesheet using the standard XSLT function document() providing an URL that starts with the scheme xslweb, for instance:

Nested pipeline
<xsl:sequence select="document('xslweb:///examples/nestedpipeline.html')"/>

where examples is the name of the webapp of the nested pipeline. The result of the nested pipeline will be available in the calling stylesheet as a document node. The nested pipeline request will follow the flow of a normal HTTP request, including the request dispatcher stylesheet. A nested pipeline call can be seen as an “internal request”, it does not go through the HTTP stack.

You can pass data to the nested pipeline using reqular request parameters added to the url, but you can also pass data as attributes using the req:set-attribute($name, $value) extension function. These attributes will be available using req:get-attribute($name) in the nested pipeline. That is especially convenient when you want to pas (sequences) of nodes without the performance penalty of serialization.

5. Development mode and production mode

In webapp.xml a web application can be configured to run in development mode or production mode. The differences between development and production mode are:

  • In development mode, compiled XSLT stylesheets are not cached. That means that for every request all stylesheets in the pipeline are reread from disk and recompiled and therefore changes will be visible immediately. In production mode, stylesheets are compiled and cached the first time they are used. However, in production mode, changes in stylesheets will automatically be detected by the file alteration monitor and the complete web application will be reloaded. So there is no need to restart the application server when deploying stylesheets in production mode. The file alteration monitor will also detect and pick up changes in the webapp.xml configuration file and plugin extension function library jars.

  • In development mode, the output of a pipeline is not streamed directly to the client (e.g. the browser) but instead buffered until the complete pipeline is executed. If an error occurs during the execution of the pipeline, the error message and stack trace are sent to the client, making it easier to debug the error. If an error occurs in production mode, only a HTTP status code 500 (internal server error) is sent to the client (that is, if the response is not already committed by the application server).

  • In development mode a pipeline step can be configured to log its (intermediate) output to the log file «xslweb-home»/logs/pipeline.log, by specifying log=”true” on the pipeline step. In production mode all logging of the output of pipeline steps is disabled.

  • In development mode, the generated XSLT of a Schematron schema is logged to the log file (with severity INFO).

6. Logging

Log files are stored in the directory «xslweb-home»/logs. This directory contains two log files, xslweb.log and pipeline.log.

Regular XSLWeb specific log messages are logged to xslweb.log. It’s also possible to write to this log file from web application stylesheets using the XPath extension function log:log(), see Logging functions.

In development mode a pipeline step can be configured to log its (intermediate) output to the log file pipeline.log, by specifying log=”true” on the pipeline step.

By default the log files are rotated when they reach the size of 10Mb, and a maximum of 8 backups is retained.

XSLWeb makes use of the standard logging framework slf4j with logback. The rotation, backup and other settings can be configured in the configuration file «xslweb-home»/config/logback.xml.

7. Response caching

Because of the upgrade of the Ehcache framework from version 2.6 to 3.9, this functionality will be removed in version 4.1 of XSLWeb (Ehcache dropped the support for SimpleCachingHeadersPageCachingFilter on which the response caching functionality was built).

The output of a pipeline can be cached by providing optional caching attributes on the element pipeline:pipeline in the stylesheet request-dispatcher.xsl. The purpose of caching the response output is to gain performance; a response that can be served from cache will be returned quicker because no transformations are necessary and also the load on the server is decreased.

The following attributes are supported:

  • cache (xs:boolean): specifies whether the output of the response must be cache. Default: false.

  • cache-key (xs:string): specifies the key under which the output of the pipeline must be cached, default the concatenation of req:method, req:request-URI and req:query-string. It is only necessary to override the default mechanism if for instance the query string contains parameters that are different for every request, like with tracking software.

  • cache-time-to-live (xs:integer): The number of seconds the output will be cached from the time it was first added to the cache. Default: 60 seconds.

  • cache-time-to-idle: (xs:integer): The number of seconds the output will be cached from the last time it was actually used. Default”: 60 seconds.

  • cache-scope (xs:string): One of “webapp” or “user”. It specifies whether the output should be cached and reused by all users of the web application (“webapp”), or for a specific user (“user”). Default “webapp”.

  • cache-headers (xs:boolean): Specifies whether XSLWeb should automatically provide the HTTP response cache headers: ETag, Last-Modified and Expires. It supports conditional GET. Because browsers and other HTTP clients have the expiry information returned in the response headers, they do not even need to request the page again. Even once the local browser copy has expired, the browser will do a conditional GET. Default: false.

XSLWeb uses the standard caching framework Ehcache to support its caching (see http://ehcache.org). More advanced configuration properties can be specified in the Ehcache specific configuration file «xslweb-home»/config/xslweb-ehcache.xml, like for instance how many responses should be cached in memory and how many on disk. See the ehcache documentation for further details.

N.B. Response caching is only enabled in production mode, see Development mode and production mode.

8. Cache busting

When a static file gets cached it can be stored for very long periods of time before it ends up expiring. This can be an annoyance in the event that you make an update to a site however, since the cached version of the file is stored in your visitors' browsers, they may be unable to see the changes made. This is due to the fact that a visitor’s browser will locally store a cached copy of your static assets given that your website is configured to leverage browser caching.

Cache busting solves the browser caching issue by using a unique file version identifier to tell the browser that a new version of the file is available. Therefore the browser doesn’t retrieve the old file from cache but rather makes a request to the origin server for the new file.

When you want to use this approach in XSLWeb, you can define an element cache-buster-id in your webapp.xml:

<webapp
  xmlns="http://www.armatiek.com/xslweb/webapp"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.armatiek.com/xslweb/webapp ../../config/xsd/xslweb/webapp.xsd">

  <title>My website</title>

  <!-- Resources to serve straight away: -->
  <resources>
    <cache-buster-id>.v1</cache-buster-id> (1)
    <resource pattern=".+\.png$" media-type="image/png" duration="P1Y"/>
  </resources>

  <!-- ... parts removed ... -->

</webapp>
1 cache-buster-id element

The cache-buster-id is then passed by XSLWeb as a stylesheet parameter to every transformation within a request dispatcher pipeline. You can use the id as part of references to static files like this:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:resp="http://www.armatiek.com/xslweb/response"
  xmlns:config="http://www.armatiek.com/xslweb/configuration"
  exclude-result-prefixes="#all"
  version="3.0">

  <xsl:output method="html" version="5.0" indent="no"/>

  <xsl:param name="config:cache-buster-id" as="xs:string?"/>

  <xsl:variable
    name="base-path"
    select="/*/req:context-path || /*/req:webapp-path"
    as="xs:string"/>

  <xsl:template name="/">
    <resp:response status="200">
      <resp:headers>
        <resp:header name="Content-Type">text/html; charset=utf-8</resp:header>
      </resp:headers>
      <resp:body>
        <html>
          <head>
            <title>My website</title>
            <link
              rel="stylesheet"
              href="{$base-path}/css/main{$config:cache-buster-id}.css"/> (1)
          </head>
          <body>
            <p class="hello">Hello World!</p>
          </body>
        </html>
      </resp:body>
    </resp:response>
  </xsl:template>

</xsl:stylesheet>
1 Use of the cache-buster-id stylesheet parameter

The cache-buster-id does not have to be part of the filename of the physical static file, XSLWeb will ignore the cache-buster-id part of the request and will serve the file without the id in the name. Therefore you can change the value of the cache-buster-id in your webapp.xml (and bust the clients caches), without touching your static resources.

Do not use the value of cache-buster-id as part of the filenames of your physical files.

9. Events

A webapp’s /xsl folder can contain an optional stylesheet named events.xsl in which xslt template rules can be specified that are executed at specific events. These events are:

  • event:webapp-open: Executed when a webapp is started/opened. In this template initialization code can be executed.

  • event:webapp-close: Executed when a webapp is stopped/closed.

  • event:webapp-reload: Executed when the webapp is reloaded, for example because a stylesheet has changed.

Events example
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:file="http://expath.org/ns/file"
  xmlns:config="http://www.armatiek.com/xslweb/configuration"
  xmlns:webapp="http://www.armatiek.com/xslweb/functions/webapp"
  xmlns:event="http://www.armatiek.com/xslweb/event"
  xmlns:xw="http://www.armatiek.com/xslweb/functions"
  exclude-result-prefixes="#all"
  version="3.0">

  <xsl:param name="config:webapp-dir" as="xs:string"/>

  <xsl:include href="../../common/xsl/lib/xslweb/xslweb.xsl"/>

  <xsl:template match="event:webapp-open">
    <xsl:call-template name="cache-valuelists"/>
  </xsl:template>

  <xsl:template match="event:webapp-close"/>

  <xsl:template match="event:webapp-reload"/>

  <xsl:template name="cache-valuelists">
    <xsl:variable name="valuelists-dir" select="$config:webapp-dir || file:dir-separator() || 'xsl' || file:dir-separator() || 'valuelists'" as="xs:string"/>
    <xsl:for-each select="file:list($valuelists-dir)">
      <xsl:variable name="valuelist-doc" select="document(xw:path-to-file-uri($valuelists-dir || file:dir-separator() || .))" as="document-node()"/>
      <xsl:variable name="valuelist-name" select="substring-before(., '.xml')" as="xs:string"/>
      <xsl:sequence select="webapp:set-attribute('valuelist-' || $valuelist-name, $valuelist-doc)"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

10. Cross Site Scripting (XSS) prevention

Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser side script, to a different end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user within the output it generates without validating or encoding it.

XSLWeb can help to prevent XSS attacks by following these steps:

  • Specify an xss-filter pipeline step as the first step in a pipeline that must be protected. The xss-filter pipeline step supports an optional attribute methods in which one or more types of data (contexts) can be specified (space separated) in which unsafe/untrusted data can occur and thefore must be encoded in the final HTML (so that it can not contain executable Javascript code). For performance reasons, the xss-filter pipeline step will only perform encoding of data if it finds potential unsafe/untrusted data in the HTTP request’s form or query string parameters. In that case the request attribute xslweb.xssfiltering is set to true(). The types of data that can be encoded, abbreviated by a two letter code, are:

    • ht: Encodes (X)HTML text content and text attributes (the default if no atribute methods is specified)

    • cs: Encodes strings in CSS

    • cu: Encodes urls in CSS

    • js: Encodes strings (variable values) in JavaScript

    • ur: Performs percent-encoding for all component of a full URI, such as a query parameter name or value, path or query-string

    • uc: Performs percent-encoding for a component of a URI, such as a query parameter name or value, path or query-string

    • xm: Encodes XML and XHTML

    • cd: Encodes data for an XML CDATA section

  • At the and of a pipeline, add a additional transformation step that adds markup around all possible unsafe/untrusted data. The markup that must be used is dependent on the type of data (see above) and has the syntax:
    '[[%ht' + text-to-encode + '%]]'

Example:

Request dispatcher stylesheet
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:req="http://www.armatiek.com/xslweb/request"
  xmlns:pipeline="http://www.armatiek.com/xslweb/pipeline"
  version="3.0">

  <xsl:template match="/req:request[req:path = '/hello-world.html']">
    <pipeline:pipeline>
      <pipeline:xss-filter methods="ht js"/>
      <pipeline:transformer name="hello-world" xsl-path="hello-world.xsl"/>
      <pipeline:transformer name="xss-encode" xsl-path="xss-encode.xsl"/>
    </pipeline:pipeline>
  </xsl:template>

</xsl:stylesheet>
xss-encode.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map"
  xmlns:req="http://www.armatiek.com/xslweb/request"
  xmlns:resp="http://www.armatiek.com/xslweb/response"
  exclude-result-prefixes="#all"
  version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <!-- Attribute names for which the values must not be encoded: -->
  <xsl:variable name="attr-names" as="map(xs:string, xs:string)">
    <xsl:map>
      <!--
      Unsafe variable data within event handler attributes must already be
      marked in prior transformation steps:
      -->
      <xsl:map-entry key="'onload'" select="''"/>
      <xsl:map-entry key="'onunload'" select="''"/>
      <xsl:map-entry key="'onchange'" select="''"/>
      <xsl:map-entry key="'onfocus'" select="''"/>
      <xsl:map-entry key="'oninput'" select="''"/>
      <xsl:map-entry key="'onselect'" select="''"/>
      <xsl:map-entry key="'onsubmit'" select="''"/>
      <xsl:map-entry key="'onkeydown'" select="''"/>
      <xsl:map-entry key="'onkeypress'" select="''"/>
      <xsl:map-entry key="'onkeyup'" select="''"/>
      <xsl:map-entry key="'onclick'" select="''"/>
      <xsl:map-entry key="'ondblclick'" select="''"/>
      <!--
      Values of the following attributes are already safe when
      encode-for-uri() is used when constructing the url:
      -->
      <xsl:map-entry key="'href'" select="''"/>
      <xsl:map-entry key="'src'" select="''"/>
      <xsl:map-entry key="'action'" select="''"/>
    </xsl:map>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:choose>
      <xsl:when test="req:get-attribute('xslweb.xssfiltering')">
        <!--
        XSLWeb has found potential unsage/untrusted data, so we must
        encode text:
        -->
        <xsl:apply-templates select="node()"/>
      </xsl:when>
      <xsl:otherwise>
        <!--
        XSLWeb has not found potential unsage/untrusted data, so there
        is no need to encode text:
        -->
        <xsl:sequence select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!--
  Leave Javascript untouched, unsafe data (ie variables) within
  javascript block and attributes must already be marked in prior
  transformation steps:
  -->
  <xsl:template match="script|@*[map:contains($attr-names, lower-case(local-name()))]" priority="2">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="'[[%ht' || . || '%]]'"/>
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:value-of select="'[[%ht' || . || '%]]'"/>
  </xsl:template>

</xsl:stylesheet>

11. Job scheduling

When you want to execute a pipeline (repeatedly) on a certain moment without user interaction, you can use the job scheduling functionality of XSLWeb. The jobs can be defined and scheduled in the webapp.xml configuration file, for example:

Job scheduling
<job>
  <name>MyJob</name>
  <uri>job/my-job</uri>
  <!-- Execute at 10:15am on the 15th day of every month: -->
  <cron>0 15 10 15 * ?</cron>
  <concurrent>false</concurrent>
</job>

The elements have the following meaning:

  • name: the name of the scheduled job. Used in log files.

  • uri: the Uri of the request to a pipeline within the current webapp. This internal request will follow the same flow of a normal HTTP request, including the request dispatcher stylesheet. The Uri does not contain the name of the webapp.

  • cron: the cron expression which is a string comprising five or six fields separated by white space that represents a set of times to execute the job (see http://en.wikipedia.org/wiki/Cron#CRON_expression).

  • concurrent: specifies whether or not the job can run concurrently with other jobs.

12. Security: authentication and authorization

For authentication and authorization XSLWeb makes use of the Java security framework Apache Shiro. Apache Shiro is a powerful, easy-to-use and "battle-tested" Java security framework that performs authentication (Basic, Token based, LDAP, JDBC, ActiveDirectory, etc), authorization (subject/role/permission based), cryptography, and session management.

If you want to use authentication mechanisms like OAuth, SAML, CAS, OpenID Connect, JWT, Kerberos (SPNEGO), REST API or authorization mechanisms like Roles/permissions, Anonymous/remember-me/(fully) authenticated, CORS, CSRF, HTTP Security headers, XSLWeb is bundled with PAC4J and especially PAC4J for Apache Shiro. This means you can configure these advanced authentication and authorization mechanisms as part of the Shiro configuration in webapp.xml.

All of the Apache Shiro documentation on https://shiro.apache.org/documentation.html and https://shiro.apache.org/web.html is relevant to the integration of Shiro in XSLWeb except for the following points:

  • https://shiro.apache.org/web.html describes how to configure Shiro in a Java webapplication using a single WebEnvironment/SecurityManager that is configured in a single INI file in /WEB-INF/shiro.ini or at the root of the class path. In XSLWeb, every webapp has it’s own WebEnvironment/SecurityManager that is configured within the security/shiro-ini section of it’s webapp.xml configuration file, for example:

Security configuration example
<?xml version="1.0"?>
<webapp
  xmlns="http://www.armatiek.com/xslweb/webapp"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:saxon-config="http://saxon.sf.net/ns/configuration"
  xsi:schemaLocation="http://www.armatiek.com/xslweb/webapp ../../config/xsd/xslweb/webapp.xsd">

  <title>Apache Shiro configuration example</title>

  <!-- ... parts removed ... -->

  <security>
    <shiro-ini><![CDATA[
[main]
shiro.loginUrl = ${webapp-path}/security/login.html
shiro.postOnlyLogout = true

authcBasic.enabled = true

sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionIdCookie.sameSite = NONE

securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false

# We need to set the cipherKey, if you want the rememberMe cookie to work after restarting or on multiple nodes.
# YOU MUST SET THIS TO A UNIQUE STRING
securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA==

[users]
# format: username = password, role1, role2, ..., roleN
root = secret,admin,user,webdav
guest = guest,guest
jdoe = test123,user

[roles]
# format: roleName = permission1, permission2, ..., permissionN
admin = *
user = portal:read,portal:write
webdav = *

[urls]
${webapp-path}/security/login.html = authc
${webapp-path}/security/secured-for-admin-role.html = authc, roles[admin]
${webapp-path}/security/secured-for-read-permission.html = authc, perms["portal:read"]
${webapp-path}/security/logout.html = logout
${webapp-path}/webdav/** = authcBasic]]>
    </shiro-ini>
  </security>

  <!-- ... parts removed ... -->

</webapp>
  • There is no need to make changes to the web.xml of XSLWeb itself, like adding a Shiro servlet filter or listener.

  • Because in the Shiro configuration all urls must be relative to the contextpath of the Java servlet, all urls must be prepended with the webapp’s name. For this you can use the variable ${webapp-name} (see the example above).

  • The Shiro web support contains a JSP/GSP tag Library. Comparable functionality of this tag library is available in XSLWeb as a set of XPath extension functions (see Security (authentication/authorization)).

12.1. Basic user authentication (deprecated)

You can implement (BASIC) user authentication by performing the following steps:

  • Include the stylesheet «xslweb-home»/xsl/system/authentication/basic/basic-authentication.xsl in your request-dispatcher.xsl stylesheet.

  • Implement the function auth:must-authenticate($request as element(request:request))): xs:boolean. In this function you can determine whether $request must be authenticated or not.

  • Implement the function auth:get-realm(): xs:string. This function must return the authentication realm.

  • Implement the function auth:login($username as xs:string, $password as xs:string): element()?. This function must authenticate $username with $password and return an empty sequence if the authentication failed or an element() containing the user profile if authentication succeeded. This element must have the name authentication and a subelement ID. The element data can be filled with arbitrary data you will need in subsequent requests.

  • This element will be stored by XSLWeb in the user’s session object under the name xslweb-userprofile so it will be available in subsequent requests.

13. Global configuration properties

In «xslweb-home»/config/xslweb.properties two global properties can be set:

  • xslweb.trustallcerts: specifies if all SSL certificates must be trusted when XSLWeb connects to an external HTTPS server.

  • xslweb.parserhardening: specifies if the Xerces XML parser must be configured to resist XML External Entity (XXE) attacks.

14. XPath extension function library

14.1. Built in extension functions

XSLWeb contains a set of readily available XPath extension functions. To use these extension functions in your XSLT stylesheets you only have to declare the namespace they are defined in.

14.2. Request functions

These functions can be used to set/read information in the HTTP request.

Namespace uri: http://www.armatiek.com/xslweb/request. In the function signatures below this namespace uri is bound to the prefix req.

Functions:

function req:set-attribute($name as xs:string, attr as item()*) as xs:boolean?

Binds an attribute to this request, using the name specified.

function req:get-attribute($name as xs:string) as item()*

Returns the attribute bound with the specified $name in this request, or an empty sequence if no attribute is bound under the name.

14.3. Response functions

These functions can be used to set/change specific information in the HTTP response. For a large part this information can also be specified in the Response XML document, see Appendix B: Response XML example for an example of a Response XML document.

Namespace uri: http://www.armatiek.com/xslweb/response. In the function signatures below this namespace uri is bound to the prefix resp.

Functions:

function response:add-cookie($cookie as element(response:cookie)) as xs:boolean?

Adds the specified HTTP cookie to the response. An example of a cookie element:

<xsl:variable name="my-cookie" as="element(resp:cookie)">
  <resp:cookie>
    <!-- Comment describing the purpose of this cookie: -->
    <resp:comment>Comment 1</resp:comment>
    <!-- The domain within which this cookie should be presented: -->
    <resp:domain>
      <xsl:value-of select="/*/req:server-name"/>
    </resp:domain>
    <!-- The maximum age in seconds for this cookie: -->
    <resp:max-age>-1</resp:max-age>
    <!-- The name of the cookie -->
    <resp:name>cookie-1</resp:name>
    <!-- The path for the cookie to which the client should
         return the cookie: -->
    <resp:path>
      <xsl:value-of select="/*/req:context-path"/>
    </resp:path>
    <!-- Indicates to the browser whether the cookie should only
         be sent using a secure protocol, such as HTTPS or SSL: -->
    <resp:is-secure>false</resp:is-secure>
    <!-- The value of the cookie -->
    <resp:value>cookie-1-value</resp:value>
    <!-- The version of the cookie protocol that this Cookie
         complies with: -->
    <resp:version>0</resp:version>
  </resp:cookie>
</xsl:variable>

XSLWeb makes uses of the Java Cookie mechanism. See Cookie.

function response:add-date-header($name as xs:string, $value as xs:dateTime) as xs:boolean?

Adds a HTTP response header with the given name and date-value.

function response:add-int-header($name as xs:string, $value as xs:integer) as xs:boolean?

Adds a HTTP response header with the given name and integer value.

function response:add-header($name as xs:string, $value as xs:string) as xs:boolean?

Adds a HTTP response header with the given name and value.

function response:encode-redirect-url($url as xs:string) as xs:string

Encodes the specified URL for use in the send-redirect function or, if encoding is not needed, returns the URL unchanged.

function response:encode-url($url as xs:string) as xs:string

Encodes the specified URL by including the session ID, or, if encoding is not needed, returns the URL unchanged.

function response:is-committed() as xs:boolean

Returns a boolean indicating if the response has been committed. A committed response has already had its status code and headers written.

function response:set-buffer-size($size as xs:integer) as xs:boolean?

Sets the preferred buffer size for the body of the response. The servlet container will use a buffer at least as large as the size requested. A larger buffer allows more content to be written before anything is actually sent, thus providing XSLWeb with more time to set appropriate status codes and headers. A smaller buffer decreases server memory load and allows the client to start receiving data more quickly. This function must be called before any response body content is written

function response:set-status($status as xs:integer) as xs:boolean?

Sets the HTTP status code for this response.

See example 5 how to use the response functions to set cookies.

14.4. Session functions

HTTP protocol and Web Servers are stateless, what it means is that for web server every request is a new request to process and they can’t identify if it’s coming from client that has been sending request previously.

But sometimes in web applications, we should know who the client is and process the request accordingly. For example, a shopping cart application should know who is sending the request to add an item and in which cart the item has to be added or who is sending checkout request so that it can charge the amount to correct client.

Session is a conversional state between client and server and it can consists of multiple request and response between client and server. Since HTTP and Web Server both are stateless, the only way to maintain a session is when some unique information about the session (session id) is passed between server and client in every request and response.

XSLWeb makes use of the session mechanism of the Java Application Server is runs on, see HttpSession.

Namespace uri: http://www.armatiek.com/xslweb/session. In the function signatures below this namespace uri is bound to the prefix session.

Functions:

function session:attribute-names() as xs:string*

Returns a sequence of strings containing the names of all attributes bound to this session.

function session:get-attribute($name as xs:string) as item()*

Returns the attribute bound with the specified $name in this session, or an empty sequence if no attribute is bound under the name.

function session:invalidate() as xs:boolean?

Invalidates this session then unbinds any attributes bound to it.

function session:set-attribute($name as xs:string, attr as item()*) as xs:boolean?

Binds an attribute to this session, using the name specified.

function session:set-max-active-interval($interval as xs:integer) as xs:boolean?

Specifies the time, in seconds, between client requests before the servlet container will invalidate this session automatically.

See example 7 how to use the session functions to set and get session attributes.

14.5. Webapp and caching functions

These functions can be used to get and set web application specific attributes and cache entries. They can be used to share session independent data between multiple requests and multiple users within one web application.

Namespace uri: http://www.armatiek.com/xslweb/functions/webapp. In the function signatures below this namespace uri is bound to the prefix webapp.

Functions:

function webapp:set-attribute($name as xs:string, attr as item()*) as xs:boolean?

Stores a sequence $attr under name $name in de webapp context, so it is available over multiple requests and for multiple users within the same webapp.

function webapp:get-attribute($name as xs:string) as item()*

Retrieves a previously stored sequence with name $name from the webapp context.

function webapp:set-cache-value($cache-name as xs:string, $key-name as xs:string, $value as item()*, $time-to-idle as xs:integer, $time-to-live as xs:integer) as xs:boolean?

Caches a sequence $value under key $key-name in the cache with name $cache-name. The cache must be configured in the webapp.xml. $time-to-idle is the maximum number of seconds an entry can exist in the cache without being accessed. The entry expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTI eviction takes place (infinite lifetime). $time-to-live is the maximum number of seconds an entry can exist in the cache regardless of use. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTL eviction takes place (infinite lifetime).

This function is deprecated, the caching framework that XSLWeb uses (Ehcache) does not (really) support tti and ttl on individual cache entries anymore. From XSLWeb 4.1 please use webapp:set-cache-value($cache-name as xs:string, $key-name as xs:string, $value as item()*) as xs:boolean? and specify the times in the configuration for a particular cache in webapp.xml.
function webapp:get-cache-value($cache-name as xs:string, $key-name as xs:string) as item()*

Retrieves a previously cached sequence with key $key-name from the cache with name $cache-name. The cache must be configured in the webapp.xml.

The information below (including section 14.5.1) does only apply to XSLWeb version 4.1 or the master of XSLWeb git.
function webapp:set-cache-value($cache-name as xs:string, $key-name as xs:string, $value as item()*) as xs:boolean?

Caches a sequence $value under key $key-name in the cache with name $cache-name. The cache must be configured in the webapp.xml.

function webapp:remove-cache-value($cache-name as xs:string, $key-name as xs:string) as xs:boolean?

Removes a previously cached sequence with key $key-name from the cache with name $cache-name.

14.5.1. Cache configuration

Caches must be configured within the webapp.xml. Each webapp.xml can contain zero or more cache configurations. The XML format that is used to configure a cache is exactly the same as the format Ehcache uses to configure a cache in XML, see XML Configuration. In fact, the configuration XML schema of Ehcache is imported in the XML schema of webapp.xml, see «xslweb-home»/config/xsd/xslweb/webapp.xsd and «xslweb-home»/config/xsd/ehcache/ehcache-core.xsd.

Example of a cache configuration:

<?xml version="1.0"?>
<webapp
  xmlns="http://www.armatiek.com/xslweb/webapp"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:saxon-config="http://saxon.sf.net/ns/configuration"
  xmlns:ehcache="http://www.ehcache.org/v3"
  xsi:schemaLocation="http://www.armatiek.com/xslweb/webapp ../../config/xsd/xslweb/webapp.xsd">

  <title>Caching example</title>
  <description>Caching example</description>
  <development-mode>true</development-mode>

  <!-- ... -->

  <ehcache:config>
    <ehcache:persistence directory="${webapp-dir}/cache"/> (1)

    <ehcache:cache-template name="default-template"> (2)
      <ehcache:key-type>java.lang.String</ehcache:key-type>
      <ehcache:value-type>java.util.ArrayList</ehcache:value-type>
      <ehcache:expiry>
        <ehcache:class>nl.armatiek.xslweb.ehcache.DefaultExpiryPolicy</ehcache:class>
      </ehcache:expiry>
      <ehcache:heap-store-settings>
        <ehcache:max-object-graph-size>9223372036854775807</ehcache:max-object-graph-size>
      </ehcache:heap-store-settings>
    </ehcache:cache-template>

    <ehcache:cache alias="my-cache" uses-template="default-template"> (3)
      <ehcache:expiry> (4)
        <ehcache:tti unit="seconds">900</ehcache:tti>
      </ehcache:expiry>
      <ehcache:resources> (5)
        <ehcache:heap unit="entries">32</ehcache:heap>
        <ehcache:offheap unit="MB">64</ehcache:offheap> <!-- Also set Java option -XX:MaxDirectMemorySize! -->
        <ehcache:disk unit="GB" persistent="true">2</ehcache:disk>
      </ehcache:resources>
    </ehcache:cache>

    <ehcache:cache alias="another-cache" uses-template="default-template">
      <!-- ... -->
    </ehcache:cache>

  </ehcache:config>

</webapp>
1 The location where cache entries are stored that must be serialized to disk.
2 The default template that cache definitions can refer to. The information in the default-template must be specified for each cache definition.
3 The definition of a cache named "my-cache". This name must be used in the extension function webapp:set-cache-value()/webapp:get-cache-value().
4 The expiry times for all cache entries within this cache definition. The time-to-idle (tti) or time-to-live can be specified here (see Ehcache documentation).
5 The configuration of the capacity of the heap memory (within the JVM), off-heap memory (outside the JVM) and disk that is used for caching entries.

Please note the following:

  • Caching in heap memory is the fastest option (especially when nodes are cached), because objects that are cached in heap memory do not have to be serialized and deserialized, unlike objects that are cached off-heap or to disk. However when serialization of nodes is necessary, XSLWeb serializes the node to a compact binary form called the Fast Infoset, that can be deserialized/parsed more quickly than the original XML document.

  • The unit of the capacity of the heap memory can be specified in number of entries, but also in B, kB, MB, GB. (off-heap and disk only in bytes). Because cache entries in the heap are not serialized, their size is far more difficult to calculate than in case of off-heap or disk memory, in which case the entries are serialized. In the case of a byte-sized heap, Ehcache must traverse the object graph of the cache entry, which in the case of XSLWeb can be a node. Byte sizing of the heap therefore has a runtime performance impact that depends on the size and graph complexity of the data cached.

If you are byte sizing the heap capacity and you receive errors like: Unable to make java.lang.ClassLoader.defineClass accessible: module java.base does not "opens java.lang" to unnamed module @1941a8ff it may be necessary to specify the following Java options:

--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED
--add-opens jdk.management.jfr/jdk.management.jfr=ALL-UNNAMED
--add-opens java.base/jdk.internal.ref=ALL-UNNAMED
--add-opens java.xml/javax.xml.catalog=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.base/java.lang.module=ALL-UNNAMED
--add-opens java.base/jdk.internal.module=ALL-UNNAMED
--add-opens java.base/jdk.internal.math=ALL-UNNAMED
Byte sizing of heap capacity currently does not work very accurate for nodes (investigation is ongoing…​). Therefore it is recommended that if you want to cache nodes to specify the capacity of heap memory in number of entries, not in bytes.
Do not forget to define in the java options the -XX:MaxDirectMemorySize option, according to the off-heap size you intend to use.

See example 7 how to use the webapp functions to set and get webapp attributes, and example 14 how to use the caching functions.

14.6. Context functions

These functions can be used to get and set “XSLWeb context” specific attributes. These can be used to share attributes between web applications.

Namespace uri: http://www.armatiek.com/xslweb/functions/context. In the function signatures below this namespace uri is bound to the prefix context.

Functions:

function context:get-attribute($name as xs:string) as item()*
function context:set-attribute($name as xs:string, attr as item()*) as xs:boolean?

See example 7 how to use the context functions to set and get context attributes.

14.7. Security (authentication/authorization)

These functions can be used to get information about the identity and authorization state of the current subject/user making te request.

Namespace uri: http://www.armatiek.com/xslweb/functions/security. In the function signatures below this namespace uri is bound to the prefix sec.

Functions:

function sec:is-guest() as xs:boolean

This function returns true() if the current Subject is considered a 'guest'. A 'guest' is any Subject that does not have an identity. That is, we don’t know who the user is because they have not logged in and they are not remembered (from 'RememberMe' services) from a previous site visit.

function sec:is-user() as xs:boolean

This function returns true() if the current Subject is considered a 'user'. A 'user' in this context is defined as a Subject with a known identity, either from a successful authentication or from 'RememberMe' services. Note that this function is semantically different from the is-authenticated() function, which is more restrictive.

function sec:is-authenticated() as xs:boolean

This function returns true() if the current Subject has successfully authenticated during their current session. It is more restrictive than the is-user() function. The is-authenticated() function will return true() only if the current Subject has successfully authenticated during their current session. It is a more restrictive function than the is-user() function, which is used to guarantee identity in sensitive workflows.

function sec:principal() as item()?

This function will return the Subject’s principal (the identifying information of the Subject, like it’s username). Without any parameters, this function will return (depending on your exact Shiro configuration in webapp.xml):

  • an empty sequence if no principal is available for the current subject

  • an xs:string if the principal is only a simple string object

  • a node() containing an XML structure of the principal’s complex information

function sec:principal($type as xs:string?) as item()?

The principal() function returns the primary principal. If you want to obtain a non-primary principal, you can acquire that principal by type. The type is a qualified Java class name, like "io.buji.pac4j.subject.Pac4jPrincipal". This function will return (depending on your exact Shiro configuration in webapp.xml):

  • an empty sequence if no principal is available for the current subject

  • an xs:string if the principal is only a simple string object

  • a node() containing an XML structure of the principal’s complex information

function sec:principal($type as xs:string?, $property as xs:string) as xs:string?

In the case the principal (either the default primary principal or 'typed' principal above) is a complex object and not a simple string, you can use this function to get a property on the principal. You can use the $property parameter to indicate the name of the property to obtain (must be accessible via a JavaBeans-compatible getter method).

Example

sec:principal('io.buji.pac4j.subject.Pac4jPrincipal', 'name')
function sec:has-role($name as xs:string+) as xs:boolean

This function will return true() if the current Subject is assigned any of the specified roles names.

function sec:has-permission($name as xs:string) as xs:boolean

This function returns true() if the current Subject 'has' (implies) the specified permission. That is, the user has the specified ability.

14.8. EXPath File

EXPath File is a standard file system API for XPath. It defines extension functions to perform file system related operations such as listing, reading, writing, copying and moving files or directories. The API is described here.

Namespace uri: http://expath.org/ns/file. In the function signatures below this namespace uri is bound to the prefix file.

Functions:

14.8.1. File properties

function file:exists($path as xs:string) as xs:boolean

Tests if the file or directory pointed by $path exists.

This function is nondeterministic.

function file:is-dir($path as xs:string) as xs:boolean

Tests if $path points to a directory. On UNIX-based systems the root and the volume roots are considered directories.

This function is nondeterministic.

function file:is-file($path as xs:string) as xs:boolean

Tests if $path points to a file.

This function is nondeterministic.

function file:last-modified($path as xs:string) as xs:dateTime

Returns the last modification time of a file or directory.

This function is nondeterministic.

Error Conditions

function file:size($file as xs:string) as xs:integer

Returns the byte size of a file, or the value 0 for directories.

This function is nondeterministic.

Error Conditions

14.8.2. Input/output

function file:append($file as xs:string, $items as item()*) as xs:boolean?
function file:append($file as xs:string, $items as item()*, $params as element(output:serialization-parameters)) as xs:boolean?

Appends a sequence of items to a file. If the file pointed by $file does not exist, a new file will be created.

$params controls the way the $items items are serialized. The semantics of $params is the same as for the fn:serialize() function in [XQuery and XPath Functions and Operators 3.0]. This consists of an output:serialization-parameters element whose format is defined in XSLT and XQuery Serialization 3.0. In contrast to fn:serialize(), the encoding stage will not be skipped by this function.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:io-error] is raised if any other error occurs.

function file:append-binary($file as xs:string, $value as xs:base64Binary) as xs:boolean?

Appends a Base64 item as binary to a file. If the file pointed by $file does not exist, a new file will be created.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:io-error] is raised if any other error occurs.

function file:append-text($file as xs:string, $value as xs:string) as xs:boolean?
function file:append-text($file as xs:string, $value as xs:string, $encoding as xs:string) as xs:boolean?

Appends a string to a file. If the file pointed by $file does not exist, a new file will be created.

The optional parameter $encoding, if not provided, is considered to be UTF-8.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:unknown-encoding] is raised if $encoding is invalid or not supported by the implementation.

  • [file:io-error] is raised if any other error occurs.

function file:append-text-lines($file as xs:string, $values as xs:string*) as xs:boolean?
function file:append-text-lines($file as xs:string, $lines as xs:string*, $encoding as xs:string) as xs:boolean?

Appends a sequence of strings to a file, each followed by the system-dependent newline character. If the file pointed by $file does not exist, a new file will be created.

The optional parameter $encoding, if not provided, is considered to be UTF-8.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:unknown-encoding] is raised if $encoding is invalid or not supported by the implementation.

  • [file:io-error] is raised if any other error occurs.

function file:copy($source as xs:string, $target as xs:string) as xs:boolean?

Copies a file or a directory given a source and a target path/URI. The following cases may occur if $source points to a file:

  1. if $target does not exist, it will be created.

  2. if $target is a file, it will be overwritten.

  3. if $target is a directory, the file will be created in that directory with the name of the source file. If a file already exists, it will be overwritten.

The following cases may occur if $source points to a directory:

  1. if $target does not exist, it will be created as directory, and all files of the source directory are copied to this directory with their existing local names.

  2. if $target is a directory, the source directory with all its files will be copied into the target directory. At each level, if a file already exists in the target with the same name as in the source, it is overwritten. If a directory already exists in the target with the same name as in the source, it is not removed, it is recursed in place (if it does not exist, it is created before recursing).

Other cases will raise one of the errors listed below.

The function returns the empty sequence if the operation is successful. If an error occurs during the operation, no rollback to the original state will be possible

This function is nondeterministic.

Error Conditions

  • [file:not-found] is raised if the $source path does not exist.

  • [file:exists] is raised if $source points to a directory and $target points to an existing file.

  • [file:no-dir] is raised if the parent directory of $source does not exist.

  • [file:is-dir] is raised if $source points to a file and $target points to a directory, in which a subdirectory exists with the name of the source file.

  • [file:io-error] is raised if any other error occurs.

function file:create-dir($dir as xs:string) as xs:boolean?

Creates a directory, or does nothing if the directory already exists. The operation will create all non-existing parent directories.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:exists] is raised if the specified path, or any of its parent directories, points to an existing file.

  • [file:io-error] is raised if any other error occurs.

function file:create-temp-dir($prefix as xs:string, $suffix as xs:string) as xs:string
function file:create-temp-dir($prefix as xs:string, $suffix as xs:string, $dir as xs:string) as xs:string

Creates a temporary directory and all non-existing parent directories and returns the full path to the created directory.

The temporary directory will not be automatically deleted after query execution. It is guaranteed to not already exist when the function is called.

If $dir is not given, the directory will be created inside the system-dependent default temporary-file directory.

This function is ·nondeterministic·.

Error Conditions

  • [file:no-dir] is raised if the specified directory does not exist or points to a file.

  • [file:io-error] is raised if any other error occurs.

function file:create-temp-file($prefix as xs:string, $suffix as xs:string) as xs:string
function file:create-temp-file($prefix as xs:string, $suffix as xs:string, $dir as xs:string) as xs:string

Creates a temporary file and all non-existing parent directories and returns the full path to the created file.

The temporary file will not be automatically deleted after query execution. It is guaranteed to not already exist when the function is called.

If $dir is not given, the directory will be created inside the system-dependent default temporary-file directory.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the specified directory does not exist or points to a file.

  • [file:io-error] is raised if any other error occurs.

function file:delete($path as xs:string) as xs:boolean?
function file:delete($path as xs:string, $recursive as xs:boolean) as xs:boolean?

Deletes a file or a directory from the file system.

If the optional parameter $recursive is set to true(), sub-directories will be deleted as well.

The function returns the empty sequence if the operation is successful.

This function is ·nondeterministic·.

Error Conditions

function file:list($dir as xs:string) as xs:string*
function file:list($dir as xs:string, $recursive as xs:boolean) as xs:string*
function file:list($dir as xs:string, $recursive as xs:boolean, $pattern as xs:string) as xs:string*

Lists all files and directories in a given directory. The order of the items in the resulting sequence is not defined. The "." and ".." items are never returned. The returned paths are relative to the provided directory $dir.

If the optional parameter $recursive is set to true(), all directories and files will be returned that are found while recursively traversing the given directory.

The optional $pattern parameter defines a name pattern in the glob syntax. If this is provided, only the paths of the files and directories whose names are matching the pattern will be returned.

An implementation must support at least the following glob syntax for the pattern:

  • * for matching any number of unknown characters and

  • ? for matching one unknown character.

A related function is file:children.

This function is nondeterministic.

  • [file:no-dir] is raised if $dir does not point to an existing directory.

  • [file:io-error] is raised if any other error occurs.

function file:move($source as xs:string, $target as xs:string) as xs:boolean?

Moves a file or a directory given a source and a target path/URI. The following cases may occur if $source points to a file:

  1. if $target does not exist, it will be created.

  2. if $target is a file, it will be overwritten.

  3. if $target is a directory, the file will be created in that directory with the name of the source file. If a file already exists, it will be overwritten.

The following cases may occur if $source points to a directory:

  1. if $target does not exist, it will be created as directory, and all files of the source directory are moved to this directory with their existing local names.

  2. if $target is a directory, the source directory with all its files will be moved into the target directory. If the target directory contains a directory with the same name as the source, the error [file:is-dir] is raised.

Other cases will raise one of the errors listed below.

The function returns the empty sequence if the operation is successful. If an error occurs during the operation, no rollback to the original state will be possible

This function is nondeterministic.

Error Conditions

  • [file:not-found] is raised if the $source path does not exist.

  • [file:exists] is raised if_$source_ points to a directory and $target points to an existing file.

  • [file:no-dir] is raised if the parent directory of $source does not exist.

  • [file:is-dir] is raised if $target points to a directory, in which a subdirectory exists with the name of the source.

  • [file:io-error] is raised if any other error occurs.

function file:read-binary($file as xs:string) as xs:base64Binary
function file:read-binary($file as xs:string, $offset as xs:integer) as xs:base64Binary
function file:read-binary($file as xs:string, $offset as xs:integer, $length as xs:integer) as xs:base64Binary

Returns the content of a file in its Base64 representation.

The optional parameters $offset and $length can be used to read chunks of a file.

This function is nondeterministic.

Error Conditions

  • [file:not-found] is raised if $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:out-of-range] is raised if $offset or $length is negative, or if the chosen values would exceed the file bounds.

  • [file:io-error] is raised if any other error occurs.

function file:read-text($file as xs:string) as xs:string
function file:read-text($file as xs:string, $encoding as xs:string) as xs:string

Returns the content of a file in its string representation.

The optional parameter $encoding, if not provided, is considered to be UTF-8.

This function is nondeterministic.

Error Conditions

function file:read-text-lines($file as xs:string) as xs:string*
function file:read-text-lines($file as xs:string, $encoding as xs:string) as xs:string*

Returns the contents of a file as a sequence of strings, separated at newline boundaries.

The optional parameter $encoding, if not provided, is considered to be UTF-8.

The newline handling is the same as for the fn:unparsed-text-lines function in [XQuery and XPath Functions and Operators 3.0].

This function is nondeterministic.

Error Conditions

function file:write($file as xs:string, $items as item()*) as xs:boolean?
function file:write($file as xs:string, $items as item()*, $params as element(output:serialization-parameters)) as xs:boolean?

Writes a sequence of items to a file. If $file already exists, it will be overwritten; otherwise, it will be created.

$params controls the way the $items items are serialized. The semantics of $params is the same as for the fn:serialize function in [XQuery and XPath Functions and Operators 3.0]. This consists of an output:serialization-parameters element whose format is defined in XSLT and XQuery Serialization 3.0. In contrast to fn:serialize, the encoding stage will not be skipped by this function.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:io-error] is raised if any other error occurs.

function file:write-binary($file as xs:string, $value as xs:base64Binary) as xs:boolean?
function file:write-binary($file as xs:string, $value as xs:base64Binary, $offset as xs:integer) as xs:boolean?

Writes a Base64 item as binary to a file. If $file already exists, it will be overwritten; otherwise, it will be created.

If the optional parameter $offset is specified, data will be written to this file position. An existing file may be resized by that operation.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:out-of-range]is raised if $offset is negative, or if it exceeds the current file size.

  • [file:io-error] is raised if any other error occurs.

function file:write-text($file as xs:string, $value as xs:string) as xs:boolean?
function file:write-text($file as xs:string, $value as xs:string, $encoding as xs:string) as xs:boolean?

Writes a string to a file. If $file already exists, it will be overwritten.

The optional parameter $encoding, if not provided, is considered to be UTF-8.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:unknown-encoding] is raised if $encoding is invalid or not supported by the implementation.

  • [file:io-error] is raised if any other error occurs.

function file:write-text-lines($file as xs:string, $values as xs:string*) as xs:boolean?
function file:write-text-lines($file as xs:string, $values as xs:string*, $encoding as xs:string) as xs:boolean?

Writes a sequence of strings to a file, each followed by the system-dependent newline character. If $file already exists, it will be overwritten; otherwise, it will be created.

The optional parameter $encoding, if not provided, is considered to be UTF-8.

The function returns the empty sequence if the operation is successful.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if the parent directory of $file does not exist.

  • [file:is-dir] is raised if $file points to a directory.

  • [file:unknown-encoding] is raised if $encoding is invalid or not supported by the implementation.

  • [file:io-error] is raised if any other error occurs.

14.8.3. Paths

function file:name($path as xs:string) as xs:string

Returns the name of a file or directory.

An empty string is returned if $path points to the root directory, or if it contains no directory separators.

This function is deterministic (no path existence check is made).

function file:parent($path as xs:string) as xs:string?

Transforms the given $path into an absolute path, as specified by file:resolve-path, and returns the parent directory.

The inverse function is file:children.

An empty sequence is returned if the path points to a root directory.

This function is nondeterministic.

function file:children($path as xs:string) as xs:string*

Returns the paths of all files and directories that are located in the given directory. The order of the items in the resulting sequence is not defined. The "." and ".." items are never returned.

The inverse function is file:parent; a related function is file:list.

This function is nondeterministic.

Error Conditions

  • [file:no-dir] is raised if $path does not point to an existing directory.

  • [file:io-error] is raised if any other error occurs.

function file:path-to-native($path as xs:string) as xs:string

Transforms a URI, an absolute path, or relative path to a canonical, system-dependent path representation. A canonical path is both absolute and unique and thus contains no redirections such as references to parent directories or symbolic links.

If the resulting path points to a directory, it will be suffixed with the system-specific directory separator.

This function is nondeterministic.

Error Conditions

  • [file:not-found] is raised if $path does not exist.

  • [file:io-error] is raised if an error occurs while trying to obtain the native path.

function file:path-to-uri($path as xs:string) as xs:anyURI

Transforms a file system path into a URI with the file:// scheme. If the path is relative, it is first resolved against the current working directory.

This function is deterministic (no path existence check is made).

function file:resolve-path($path as xs:string) as xs:string

Transforms a relative path into an absolute operating system path by resolving it against the current working directory.

If the resulting path points to a directory, it will be suffixed with the system-specific directory separator.

This function is nondeterministic.

14.8.4. System properties

function file:dir-separator() as xs:string

Returns the value of the operating system-specific directory separator, which usually is / on UNIX-based systems and \ on Windows systems.

This function is nondeterministic.

function file:line-separator() as xs:string

Returns the value of the operating system-specific line separator, which usually is on UNIX-based systems, on Windows systems and on Mac systems.

This function is nondeterministic.

function file:path-separator() as xs:string

Returns the value of the operating system-specific path separator, which usually is : on UNIX-based systems and ; on Windows systems.

This function is nondeterministic.

function file:temp-dir() as xs:string

Returns the path to the default temporary-file directory of an operating system.

This function is nondeterministic.

function file:base-dir() as xs:string

Returns the parent directory of the static base URI. If the Base URI property is undefined, the empty sequence is returned. - If a static base URI exists, and if points to a local file path, this function returns the same result as the expression file:parent(static-base-uri()).

Summary of Error Conditions

  • file:not-found
    The specified path does not exist.

  • file:invalid-path
    The specified path is invalid.

  • file:exists
    The specified path already exists.

  • file:no-dir
    The specified path does not point to a directory.

  • file:is-dir
    The specified path points to a directory.

  • file:unknown-encoding
    The specified encoding is not supported.

  • file:out-of-range
    The specified offset or length is negative, or the chosen values would exceed the file bounds.

  • file:io-error
    A generic file system error occurred.

The structure of element(output:serialization-parameters) is described in XSLT and XQuery Serialization 3.0.

See example 10 how to use some of the EXPath File functions.

14.9. EXPath HTTP Client

These functions are an implementation of the specification: EXPath - HTTP Client Module based on the Java HTTP client library OkHttp. It is an XSLWeb “native” implementation. The API defines one extension function to perform HTTP requests and handle responses.

EXPath HTTP Client provides a lot more functionality than XSLT’s document() function:

  • Execution of other HTTP methods (POST, HEAD, PUT, DELETE etc), making it possible to consume both SOAP and REST based web services.

  • Request text or even binary documents.

  • Authentication (Basic and Digest).

  • Specify HTTP headers in the request and read the HTTP headers of the response.

  • Execute requests to HTML pages and parse them as well-formed XML.

Not implemented at this time:

  • Multipart responses (multipart requests are supported)

  • Other authentication methods than "Basic"

Extensions to the specifications:

  • Proxy server support via the attributes "http:request/@proxy-host","http:request/@proxy-port", "http:request/@proxy-username", "http:request/@proxy-password"

  • Trust all SSL certificates via the attribute http:request/@trust-all-certs (xs:boolean, default: false())

Further remarks:

  • The default timeout (connect/write/read/cal) is 30 seconds (can be changed via http:request/@timeout)

  • Certificate authorities of the host platform are trusted

Namespace uri: http://expath.org/ns/http-client. In the function signatures below this namespace uri is bound to the prefix http.

Functions:

function http:send-request($request as element(http:request)?, $href as xs:string?, $bodies as item()*) as item()+
  • $request contains the various parameters of the request, for instance the HTTP method to use or the HTTP headers. Among other things, it can also contain the other param’s values: the URI and the bodies. If they are not set as parameter to the function, their value in $request, if any, is used instead. See Sending a request for the detailed definition of the http:request element. If the parameter does not follow the grammar defined in this spec, this is an error [err:HC005].

  • $href is the HTTP or HTTPS URI to send the request to. It is an xs:anyURI, but is declared as a string to be able to pass literal strings (without requiring to explicitly cast it to an xs:anyURI).

  • $bodies is the request body content, for HTTP methods that can contain a body in the request (e.g. POST). This is an error if this param is not the empty sequence for methods that must be empty (e.g. DELETE). The details of the methods are defined in their respective specs (e.g. RFC 2616 or RFC 4918). In case of a multipart request, it can be a sequence of several items, each one is the body of the corresponding body descriptor in $request. See below for details.

Besides the 3-params signature above, there are 2 other signatures that are convenient shortcuts (corresponding to the full version in which corresponding params have been set to the empty sequence). They are:

function http:send-request($request as element(http:request)?, $href as xs:string?) as item()+
function http:send-request($request as element(http:request)) as item()

Summary of Error Conditions

  • err:HC001
    An HTTP error occurred.

  • err:HC002
    Error parsing the entity content as XML or HTML.

  • err:HC003
    With a multipart response, the override-media-type must be either a multipart media type or application/octet-stream.

  • err:HC004
    The src attribute on the body element is mutually exclusive with all other attribute (except the media-type).

  • err:HC005
    The request element is not valid.

  • err:HC006
    A timeout occurred waiting for the response.

See examples 11 and 21 how to use some of the EXPath HTTP Client function.

14.10. Base64

Namespace uri: http://www.armatiek.com/xslweb/functions/base64. In the function signatures below this namespace uri is bound to the prefix base64.

Functions:

function base64:encode($str as xs:string) as xs:string

Encodes a string using the base64 algorithm but does not chunk the output.

function base64:decode($str as xs:string) as xs:string

Decodes a Base64 string into octets which then are converted to a UTF-8 string.

14.11. Execute external processes

Namespace uri: http://www.armatiek.com/xslweb/functions/exec. In the function signatures below this namespace uri is bound to the prefix external.

Functions:

function external:exec-external(
  $command-line as xs:string,
  $args as xs:string*,
  $time-out as xs:integer?,
  $async as xs:boolean?,
  $work-dir as xs:string?,
  $handle-quoting as xs:boolean?,
  $log-stdout-and-stderr as xs:boolean?,
  $environment-variables as map(*)?) as xs:integer?
  • $command-line: the path to the executable.

  • $args: a sequence of arguments to the application.

  • $time-out: the time in milliseconds after an asynchronous process is killed.

  • $async: indicates if the process must be started asynchronous or not.

  • $work-dir: the working directory for the subprocess.

  • $handle-quoting: indicates if the function should automatically handle the quoting (the default) or not.

  • $log-stdout-and-stderr: write stdout/stderr of the executable also to the log file.

  • $environment-variables: a optional map of environment variables for the subprocess.

The return value is an empty sequence in case of an asynchronous process. In case of a synchronous process the return value is the exit value returned by the process.

14.12. Logging functions

Logging functionality. The logging information is written to the main XSLWeb log file, default «xslweb-home»/logs/xslweb.log.

Namespace uri: http://www.armatiek.com/xslweb/functions/log. In the function signatures below this namespace uri is bound to the prefix log.

Functions:

function log:log($level as xs:string, $message as item()*) as xs:boolean
function log:log(
  $level as xs:string,
  $message as item()*,
  $params as element(output:serialization-parameters)) as xs:boolean

Where $level is one of “ERROR”, “WARN”, “INFO” or “DEBUG”. The structure of element(output:serialization-parameters) is described in XSLT and XQuery Serialization 3.0. See example 15 how to use some of the log functions.

The next set of logging functions can be used in cases wher

function log:trace(
  $output as item()*,
  $message as item()*,
  $params as element(output:serialization-parameters)) as item()*

function log:debug(
  $output as item()*,
  $message as item()*,
  $params as element(output:serialization-parameters)) as item()*

function log:info(
  $output as item()*,
  $message as item()*,
  $params as element(output:serialization-parameters)) as item()*

function log:warn(
  $output as item()*,
  $message as item()*,
  $params as element(output:serialization-parameters)) as item()*

function log:error(
  $output as item()*,
  $message as item()*,
  $params as element(output:serialization-parameters)) as item()*

14.13. Email

Functionality for sending e-mail via SMTP.

Namespace uri: http://www.armatiek.com/xslweb/functions/email. In the function signatures below this namespace uri is bound to the prefix email.

Functions:

function email:send-mail($email as element(email:email)) as xs:boolean

See example 12 how to use the send-email example and an example of the structure of element(email:email).

14.14. Serialization

Functionality for serializing a node to a string.

Namespace uri: http://www.armatiek.com/xslweb/functions/serialize. In the function signatures below this namespace uri is bound to the prefix ser.

Functions:

function ser:serialize($nodes as node()*, $options as element(output:serialization-parameters)?) as xs:string

The structure of element(output:serialization-parameters) is described in XSLT and XQuery Serialization 3.0.

14.15. Caching

Namespace uri: http://www.armatiek.com/xslweb/functions/cache. In the function signatures below this namespace uri is bound to the prefix cache.

Functions:

Remove a cache entry from the response output cache:

function cache:remove($cache-key as xs:string) as xs:boolean?

14.16. Image processing

Namespace uri: http://www.armatiek.com/xslweb/functions/image. In the function signatures below this namespace uri is bound to the prefix img.

Functions:

function img:scale($source as xs:string, $target as xs:string, $format-name as xs:string, $target-size as xs:integer) as xs:boolean?

Resizes an image and optionally convert it to another format. $source is the path or url to the source image, $target the path to the scaled image, $format-name the name of the target format (like png, gif, jpg) and $target-size the maximum image width or height of the scaled image.

14.17. Input/Output

Namespace uri: http://www.armatiek.com/xslweb/functions/io. In the function signatures below this namespace uri is bound to the prefix io.

Functions:

function io:register-temp-file($path as xs:string) as xs:boolean?

Registers a temporary file or directory that will automatically be deleted after the pipeline has executed.

14.18. Utilities

Namespace uri: http://www.armatiek.com/xslweb/functions/util. In the function signatures below this namespace uri is bound to the prefix util.

Functions:

function util:discard-document($document-node()) as document-node()

Remove supplied document from memory pool so it will be released by the Java garbage collector.

function util:parse-html($html as xs:string) as document-node()

This function takes a single argument, a string containing the source text of an HTML document. It returns the document node (root node) that results from parsing this text using the TagSoup HTML parser.

function util:parse($serialized-xml as xs:string) as document-node()

Parse a XML string to a document node.

14.19. Zip/Unzip

(Un)zip functionality

Namespace uri: http://www.armatiek.com/xslweb/functions/zip. In the function signatures below this namespace uri is bound to the prefix zip.

Functions:

function zip:zip($source as xs:string, $target as xs:string) as xs:boolean?

Zip a file on path $source to a new file on path $target:

function zip:unzip($source as xs:string, $target as xs:string) as xs:boolean?

Unzip a file on path or url $source to a new file on path $target:

14.20. UUID

Namespace uri: http://www.armatiek.com/xslweb/functions/uuid. In the function signatures below this namespace uri is bound to the prefix uuid.

Functions:

function uuid:uuid() as xs:string

Generate a type 4 (pseudo randomly generated) universally unique identifier.

14.21. JSON

(Experimental)

Namespace uri: http://www.armatiek.com/xslweb/functions/json. In the function signatures below this namespace uri is bound to the prefix json.

Functions:

function json:serialize-json($items as item()*) as xs:string

Serializes a sequence of items to a JSON representation.

function json:parse-json($json as xs:string) as document-node()?

Parses a JSON string to a document node.

function json:escape-json($str as xs:string) as xs:string?

Escapes the characters in $str using JSON string rules.

function json:unescape-json($json as xs:string) as xs:string?

Unescapes any JSON literals found in $json.

Alternative: now you can also use the standard XPath 3.1 functions, see: https://www.w3.org/TR/xpath-functions-31/#json-functions.

14.22. Dynamic (scripted) extension functions

Namespace uri: http://www.armatiek.com/xslweb/functions/dynfunc. In the function signatures below this namespace uri is bound to the prefix dynfunc.

Functions:

function dynfunc:register($java-classes as xs:string+) as element(function:diagnostics)?

This function registers (makes available within an XSLWeb web application) one or more extension functions that are implemented in one or more Java classes defined in $java-classes. Methods that implement an extension function must be annotated with the Java annotation ExtensionFunction, for instance:

import nl.armatiek.xslweb.saxon.functions.dynfunc.ExtensionFunction;
import org.apache.commons.lang3.StringUtils;

public class MyExtensionFunctions {

  /* Adds two integers: */
  @ExtensionFunction(
      uri="http://example.com/functions/test",
      name="add",
      hasSideEffects=false)
  public int add(int x, int y) {
    return x + y;
  }

  /* Removes diacritics from a string. The case will not be altered: */
  @ExtensionFunction(
      uri="http://example.com/functions/test",
      name="strip-accents",
      hasSideEffects=false)
  public String stripAccents(String text) {
    return StringUtils.stripAccents(text);
  }

}

The annotation defines the namespace uri and local name of the (qualified name of the) extension function on the XPath side and whether the extension function has side effects.

The Java code needs to be registered only once. After registering, the extension functions will be available in further XSLT transformations that are executed within the XSLWeb webapp in which the code was registered. Therefore the template event:webapp-open in events.xsl is a suitable location to register dynamic extension functions.

The Java classes must not contain a package declaration. All Java classes are supposed to be in the same (WebApp specific) package which is automatically added to the code before compilation.

The arguments and return type of the methods that implement an extension function must be of one of the following primitive types, interfaces or classes or arrays thereof:

Java class Equivalent XPath type

boolean

xs:boolean

Boolean

xs:boolean?

String

xs:string?

CharSequence

xs:string?

long

xs:integer

Long

xs:integer?

int

xs:integer

Integer

xs:integer?

short

xs:short

Short

xs:short?

byte

xs:byte **

Byte

xs:byte?

float

xs:float

Float

xs:float?

double

xs:double

Double

xs:double?

java.net.URI

xs:anyURI?

java.net.URL

xs:anyURI?

java.math.BigInteger

xs:integer?

java.math.BigDecimal

xs:decimal?

java.util.Date

xs:dateTime?

net.sf.saxon.s9api.QName

xs:QName

net.sf.saxon.value.StringValue

xs:string?

net.sf.saxon.value.BooleanValue

xs:boolean?

net.sf.saxon.value.DoubleValue

xs:double?

net.sf.saxon.value.FloatValue

xs:float?

net.sf.saxon.value.DecimalValue

xs:decimal?

net.sf.saxon.value.IntegerValue

xs:integer?

net.sf.saxon.value.AnyURIValue

xs:anyURI?

net.sf.saxon.value.QNameValue

xs:QName?

net.sf.saxon.value.DateValue

xs:date?

net.sf.saxon.value.DateTimeValue

xs:dateTime?

net.sf.saxon.value.TimeValue.class

xs:time?

net.sf.saxon.value.DurationValue

xs:duration?

net.sf.saxon.value.DayTimeDurationValue

xs:duration?

net.sf.saxon.value.YearMonthDurationValue

xs:duration?

net.sf.saxon.value.GYearValue

xs:gYear

net.sf.saxon.value.GYearMonthValue

xs:gYearMonth

net.sf.saxon.value.GMonthValue

xs:gMonth

net.sf.saxon.value.GMonthDayValue

xs:gMonthDay

net.sf.saxon.value.GDayValue

xs:gDay

net.sf.saxon.value.Base64BinaryValue

xs:base64Binary

net.sf.saxon.value.HexBinaryValue

xs:hexBinary

net.sf.saxon.om.Function

function()

net.sf.saxon.ma.map.MapItem

map(*)

net.sf.saxon.om.NodeInfo

node(), element(), attribute(), text(), comment(), processing-instruction()

net.sf.saxon.om.TreeInfo

document-node()

net.sf.saxon.expr.XPathContext

The current Saxon XPathContext object *

nl.armatiek.xslweb.configuration.Context

The current XSLWeb Context object *

nl.armatiek.xslweb.configuration.WebApp

The current XSLWeb WebApp object *

javax.servlet.http.HttpSession

The current JEE session object *

javax.servlet.http.HttpServletRequest

The current JEE request object *

javax.servlet.http.HttpServletResponse

The current JEE response object *

() These objects does not have to be specified in the XPath function call, only as arguments in the Java call method. These arguments must be specified first, before arguments that are specified in the XPath function call.
(
*) byte[] maps to xs:unsignedByte*

When the extension function is called from XPath, the equivalent XPath type (from the second column) must be used.

If the registration and code compilation succeeds, the function will return an empty sequence. When there are errors or warnings during code compilation, the function will return an element function:diagnostics. An example of such diagnostics element is:

<dynfunc:diagnostics>
  <dynfunc:diagnostic
    code="compiler.err.cant.resolve.location"
    line="8"
    column="46"
    start="255"
    end="280"
    kind="ERROR"
    message="cannot find symbol
    symbol:   class LongestCommonSubsequenceX
    location: class Example21"
    position="255"/>
</dynfunc:diagnostics>

Within the extension function code all Java classes from the classpath of the XSLWeb Java web application can be used or imported.

The function can raise an exception with one of the following error codes:

Code Description

err:DF001

Generic error compiling and registering extension function classes, %s

err:DF002

No Java compiler could be found. Are you using a JDK version of the Java platform?

err:DF003

Could not determine classname from code unit %d

function dynfunc:is-registered($function-name as xs:QName) as xs:boolean

This function can be used to query if a function with name $function-name is already registered within the current XSLWeb web application. The standard XSLT functions fn:function-available() can also be used.

dynfunc:call($function-name as xs:QName, $arg-1 as item()* ... $arg-n as item()*) as item()*

This function can be used to execute a extension function that was registered before using the function dynfunc:register. The first argument is the qualified name of the function, the other arguments are the function parameters.

The function can raise an exception with one of the following error codes:

Code Description

err:DF010

No function with name %s is registered

err:DF011

The number of supplied arguments in the call to the XPath extension function %s (%d) does not match the number of declared arguments in the Java method (%d)[ , considering %d implicit objects]

err:DF012

Conversion from %s to %s is not supported

err:DF013

A wrapped Java InvocationTargetException, IllegalAccessException, IllegalArgumentException or InstantiationException

A complete example:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:dynfunc="http://www.armatiek.com/xslweb/functions/dynfunc"
  xmlns:err="http://www.w3.org/2005/xqt-errors"
  exclude-result-prefixes="#all"
  version="3.0">

  <xsl:template name="my-template">

    <xsl:variable name="code-units" as="xs:string+">
      <xsl:text>
      <![CDATA[
      import nl.armatiek.xslweb.saxon.functions.dynfunc.ExtensionFunction;
      import org.apache.commons.lang3.StringUtils;

      public class MyExtensionFunctions {

        /* Removes diacritics from a string. The case will not be altered: */
        @ExtensionFunction(
            uri="http://example.com/functions/test",
            name="strip-accents",
            hasSideEffects=false)
        public String stripAccents(String text) {
          return StringUtils.stripAccents(text);
        }

      }
      ]]>
      </xsl:text>
    </xsl:variable>

    <!-- Register the extension functions in the Java code: -->
    <xsl:variable
      name="result"
      select="dynfunc:register($code-units)"
      as="element(dynfunc:diagnostics)?"/>

    <xsl:choose>
      <!-- Check the registration result: -->
      <xsl:when test="$result/dynfunc:diagnostic/@kind = 'ERROR'">
        <xsl:sequence select="$result"/>
      </xsl:when>
      <xsl:otherwise>
        <!-- Call the functions: -->
        <xsl:try>
          <xsl:variable
            name="function-name"
            select="QName('http://example.com/functions/test', 'strip-accents')"
            as="xs:QName"/>
          <xsl:sequence select="dynfunc:call($function-name, 'naïve fiancé')"/>
          <xsl:catch>
            <xsl:value-of select="$err:description"/>
          </xsl:catch>
        </xsl:try>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

14.23. Javascript extension function

Namespace uri: http://www.armatiek.com/xslweb/functions/script. In the function signatures below this namespace uri is bound to the prefix script.

Functions:

function script:invoke(
  $javascript as xs:string,
  $function-name as xs:string,
  $arg-1 as xs:anyAtomicType*,
  $arg-2 as xs:anyAtomicType*,
  .... ,
  $arg-n as xs:anyAtomicType*) as xs:anyAtomicType*

This function executes the function $function-name within the Javascript code $javascript. The arguments of the Javascript function can be specified by $arg-1 to $arg-n. Each argument can be a single atomic type or an array of atomic types. The arguments must be atomic types that can be converted to a Java/Javascript object equivalent. The result of the Javascript function must be a single object or an array of objects that can be converted back to a (sequence of) atomic type(s) (see Default data type mapping).

When XSLWeb calls the Javascript function four more arguments (Java objects) are passed to the actual Javascript function: the global XSLWeb Context object, the current Webapp object, the HttpServletRequest object and the HttpServletResponse object.

XSLWeb uses the Java Nashorn scripting engine to execute the Javascript code.

A complete example:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:script="http://www.armatiek.com/xslweb/functions/script"
  exclude-result-prefixes="#all"
  version="3.0">

  <!-- ... -->

  <xsl:template name="my-template">

    <xsl:variable name="script" as="xs:string">
      <![CDATA[function getLengthsOfStrings(context, webapp, request, response, arrayOfStrings) {
        var arrayOfInteger = new Array();
        for (i = 0; i < arrayOfStrings.length; i++) {
          arrayOfInteger.push(arrayOfStrings[i].length());
        }
        return arrayOfInteger;
      }
      ]]>
    </xsl:variable>

    <xsl:sequence select="script:invoke($script, 'getLengthsOfStrings', ('France', 'Germany', 'Spain'))"/>

  </xsl:template>

  <!-- ... -->

</xsl:stylesheet>
With the release of Java 11, Nashorn is deprecated, and has been removed from JDK 15 onwards, but OpenJDK Nashorn supports Java starting with version 15.3.

See also example 20 of the examples webapp.

14.24. SQL

Functionality to query data from and store data in a relational database. These extension functions make use of the Java JDBC framework. You can place the JDBC driver for your database in the directory «xslweb-home»/common/lib (restart required). Then you can define a datasource in the datasources section of the application’s webapp.xml, for instance for a H2 database:

<datasource>
  <name>my-datasource</name>
  <driver-class>org.h2.Driver</driver-class>
  <jdbc-url>jdbc:h2:file://${webapp-dir}/database/my-database.mv</jdbc-url>
  <property name="user">sa</property>
</datasource>

With the extension function sql:get-connection(“my-datasource”) a database connection can be retrieved from the connection pool. This connection then can be used to execute one or more queries.

The connection pool is implemented using the Java connection pool framework c3p0. The c3p0 specific properties that can be used in the datasource definition in webapp.xml are described here. De default c3p0 properties can be configured in the configuration file «xslweb-home»/config/c3p0-config.xml.

Connections and resultsets can be explicitly closed by using the extension function sql:close(). The sql:close() function on a connection will also return the connection to the connection pool. Connections and resultsets that are not closed that way will be implicitly closed at the end of the pipeline, and connections will be returned to the connection pool automatically.

JDBC drivers are available for most relational database systems (like MySQL, Oracle, PostgreSQL, MSSQL Server, Sybase, Cloudscape and Firebird), but also for non-relational database datasources like CSV files and LDAP directory services (untested).

Namespace uri: http://www.armatiek.com/xslweb/functions/sql. In the function signatures below this namespace uri is bound to the prefix sql.

Functions:

function sql:close($connection as java.sql.Connection) as xs:boolean?

Closes a database connection.

function sql:close($resultset as java.sql.ResultSet) as xs:boolean?

Closes a result set.

function sql:commit($connection as java.sql.Connection) as xs:boolean?

Makes all changes made since the previous commit/rollback permanent and releases any database locks currently held by this Connection object.

function sql:execute-query($connection as java.sql.Connection, $sql as xs:string) as java.sql.ResultSet

Executes query $sql and returns the result of the query as a Java ResultSet object. This ResultSet can then be passed to the function sql:get-next-row() to iterate through the records of the resultset, or sql:resultset-to-node() to get an XML representation of the complete resultset.

function sql:get-connection(
  $name as xs:string,
  $username as xs:string?,
  $password as xs:string?,
  $readonly as xs:boolean?,
  $autocommit as xs:boolean?) as java.sql.Connection

Creates or gets a database connection. XSLWeb uses a connection pool for fast creation and reuse of database connections. This function returns a Java Connection object that can be passed as a parameter to other sql functions. $name is the name of a datasource specification in the webapp.xml of the web application.

function sql:get-next-row($resultset as java.sql.ResultSet) as xs:anyAtomicType*

Returns the next row in a Java ResultSet represented as a sequence of atomic types.

function sql:rollback($connection as java.sql.Connection) as xs:boolean?

Undoes all changes made in the current transaction and releases any database locks currently held by this Connection.

function sql:resultset-to-node($resultset as java.sql.ResultSet) as element()

Returns an XML representation of the complete ResultSet.

See example 22 how to use some of the sql extension functions.

14.25. Transformation

Namespace uri: http://www.armatiek.com/xslweb/functions/transformation. In the function signatures below this namespace uri is bound to the prefix tf.

Functions:

function tf:transform($path as xs:string, $nodes as node()+, $options as map(*)) as document-node()

Transforms $nodes using the XSLT stylesheet on $path with $options to the result document, using the first node in $nodes as global context item. $path is a path relative to the webapp’s /xsl directory. $options currently only supports the option stylesheets-params.

This function can be used everywhere the standard XPath 3.1 function fn:transform can be used, but this function will cache the compiled XSLT stylesheet, all XSLWeb XPath extension functions will be available in the transformation, and debugging of the stylesheet is supported.

Example:

<xsl:variable name="my-doc" as="document-node()">
  <xsl:document>
    <p>Hello World</p>
  </xsl:document>
</xsl:variable>

<xsl:variable name="options" as="map(xs:string, item()*)">
  <xsl:map>
    <xsl:map-entry key="'stylesheet-params'">
      <xsl:map>
        <xsl:map-entry key="QName((),'my-param-1')" select="'my-value-1'"/>
        <xsl:map-entry key="QName((),'my-param-2')" select="15"/>
      </xsl:map>
    </xsl:map-entry>
  </xsl:map>
</xsl:variable>
<xsl:sequence select="tf:transform('my-transformation.xsl', $my-doc, $options)"/>

14.26. Custom extension functions

It is also possible to write your own custom XPath extension functions in Java and add them to an XSLWeb web application. These extension functions must be integrated extension functions that use the full interface of Saxon version 10.6.

The compiled jar of a custom extension function together with any libraries that the function depend on can be placed in the folder «web-app»/lib. There is no need to restart the application server, XSLWeb will detect the jars and will load and register the extension function automatically.

14.27. Extension functions with side effects

A number of the extension functions described in previous sections perform a certain task and thereby change the state of something outside the stylesheet, like write or log to a file, send an e-mail etc. These functions don’t have any return information and should have an empty sequence as their return type. In XSLWeb, the return type of these functions is actually declared as xs:boolean? The reason is that in that case the Saxon XSLT optimizer cannot ignore these functions, because they could add something to the result tree (a boolean value). In reality, these functions never return this boolean value and always return an empty sequence. Therefore it is safe to do something like:

<xsl:sequence select="log:log('INFO', 'Hello World!')"/>

without having to worry that something is written to the result tree.

15. WebDAV access

XSLWeb provides Web Distributed Authoring and Versioning (WebDAV) access to the webapp resources, like the XSLT stylesheets, the webapp.xml and the static resources like css and javascript files. WebDAV is an extension of the Hypertext Transfer Protocol (HTTP) that allows clients to perform remote Web content authoring operations. WebDAV is defined in RFC 4918 by a working group of the Internet Engineering Task Force. See also WebDAV on Wikipedia.

To use the WebDAV protocol you need a WebDAV client like CarotDAV (Windows - Free), Bitkinex (Windows), Netdrive (Windows;macOS), cadaver (Linux - Free) or the WebDAV data source within the <oXygen/> XML Editor (Linux;macOS;Windows).

Every XSLWeb webapp has it’s own WebDAV configuration. WebDAV access is disabled by default. You can enable WebDAV access in the webapp.xml within the element <webdav>:

<?xml version="1.0"?>
<webapp
  xmlns="http://www.armatiek.com/xslweb/webapp"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:saxon-config="http://saxon.sf.net/ns/configuration"
  xsi:schemaLocation="http://www.armatiek.com/xslweb/webapp ../../config/xsd/xslweb/webapp.xsd">

  <title>WebDAV access example</title>

  <!-- ... parts removed ... -->

  <webdav>
    <enabled>true</enabled>
    <lazy-folder-creation-on-put>true</lazy-folder-creation-on-put> (1)
  </webdav>

  <!-- ... parts removed ... -->

</webapp>
1 Overriding RFC 2518, the folders of resources being created, can be created too if they do not exist.

You can access the top-level WebDAV folder using the url http://my-domain:my-port/my-webapp/webdav/, for instance http://localhost:8080/examples/webdav/.

When you enable WebDAV access you probably want to authenticate users before accessing the resources, see Security: authentication and authorization. By default there is no authentication. If a security/shiro-ini configuration is present in webapp.xml, users must have role "webdav" to be able to access the WebDAV functionality.

Example how to configure authentication for WebDAV requests:

<?xml version="1.0"?>
<webapp
  xmlns="http://www.armatiek.com/xslweb/webapp"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:saxon-config="http://saxon.sf.net/ns/configuration"
  xsi:schemaLocation="http://www.armatiek.com/xslweb/webapp ../../config/xsd/xslweb/webapp.xsd">

  <title>WebDAV access example</title>

  <!-- ... parts removed ... -->

  <webdav>
    <enabled>true</enabled>
    <lazy-folder-creation-on-put>true</lazy-folder-creation-on-put> (1)
  </webdav>

  <security>
    <shiro-ini><![CDATA[
[main]
authcBasic.enabled = true

sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionIdCookie.sameSite = NONE

securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false

[users]
# format: username = password, role1, role2, ..., roleN
webdavuser = secret,webdav

[roles]
# format: roleName = permission1, permission2, ..., permissionN
webdav = *

[urls]
${webapp-path}/webdav/** = authcBasic]]>
    </shiro-ini>
  </security>

  <!-- ... parts removed ... -->

</webapp>

16. Installation

The sources of XSLWeb can be found on GitHub. XSLWeb is licensed under the Apache License version 2.0.

Compiled binaries of XSLWeb 4.2.0 can be downloaded in two distributions:

  1. As a .zip (Windows) or .tar.gz (Linux/macOS) archive, containing a ready-to-run installation of XSLWeb.

  2. As a standard web application archive (.war) which can be installed on your favorite Java Application Server.

16.1. Installation of the ready-to-run distribution

16.1.1. Download

The ready-to-run distribution can be downloaded from:

Apart from the .war file, each distribution file contains a platform specific custom OpenJDK version 13 JRE, the Jetty Java application server and several platform specific batch files or shell scripts.

16.1.2. Unpack

After downloading, the distribution file may be unzipped/untarred in a directory of your choice, under Windows and macOS by double clicking on the file and under Linux and macOS by running the following command in a shell:

Unpacking the zipped tar file on Linux
tar -xzvf xslweb-v{xslweb-version}-linux-x64.tar.gz
Unpacking the zipped tar file on macOS
tar -xzvf xslweb-v{xslweb-version}-osx-x64.tar.gz

In case the browser already zipped the file during the download process, the commands are:

Unpacking the tar file on Linux
tar -xvf xslweb-v{xslweb-version}-linux-x64.tar
Unpacking the tar file on macOS
tar -xvf xslweb-v{xslweb-version}-osx-x64.tar
Do not install the XSLWeb distribution in a directory that contains spaces.

16.1.3. Run and install

XSLWeb can be started by running the batch file <install-dir>\bin\run-service.bat (Windows) or <install-dir>/bin/run-service.sh (*nix). After the service is started, the correct installation can be tested by opening a browser and going to the url http://localhost:8152. XSLWeb can be stopped by running the corresponding batch file stop-service.bat or shell script stop-service.sh from another command prompt or shell.

To increase or decrease the Java heap memory size that is available to the XSLWeb process please alter the variable MAXMEM in the file <install-dir>\bin\service\config.cmd (Windows) or <install-dir>/bin/run-service.sh (Linux and macOS). The default is 256m (megabytes).
If you get an error message on macOS like "java cannot be opened because the developer cannot be verified" you must remove the “quarantine” attribute from the java executable (which tells the system that java should not be run automatically before being explicitly approved by the user). This can be done running a command like:
xattr -d com.apple.quarantine /path-to-xslweb/xslweb-ready-to-run-v4.0.0-osx-x64/jre/bin/java

16.2. Optional: install XSLWeb as a Windows service or Linux/macOS daemon

The following sections explain how XSLWeb can be installed as an "always-on" Windows service or Linux/macOS daemon.

16.2.1. Linux

The way XSLWeb can be installed as a daemon on Linux is dependent on the (version of) the Linux distribution and in particular the init system that it uses. The three most common init systems are:

  • System V, for instance on:

    • Debian 6 and earlier

    • Ubuntu 9.04 and earlier

    • CentOS 5 and earlier

  • Upstart, for instance on:

    • Ubuntu 9.10 to Ubuntu 14.10, including Ubuntu 14.04

    • CentOS 6

  • systemd is the init system for the most recent distributions:

    • Debian 7 and Debian 8

    • Ubuntu 15.04 and newer

    • CentOS 7

Only the installation for the systemd init system is discussed in this manual. When your distribution uses another init system than systemd, please consult the documentation for that particular distribution and init system.

Execute the following steps:

  • Give the script file execute permissions by executing the following command (replace <install-dir>):

chmod +x <install-dir>/bin/run-service.sh
  • Create a unit file /etc/systemd/system/xslweb.service as root with the content below (replace <install-dir>):

[Unit]
Description=XSLWeb

[Service]
ExecStart=/bin/bash <install-dir>/bin/run-service.sh
ExecStop=/bin/bash <install-dir>/bin/stop-service.sh
WorkingDirectory=<install-dir>/bin
Restart=always

[Install]
WantedBy=multi-user.target
  • Change the access permissions of the unit file :

sudo chmod 644 /etc/systemd/system/xslweb.service
  • Use the 'enable' command to ensure that the service starts whenever the system boots:

sudo systemctl enable xslweb
  • Reload the unit file:

sudo systemctl daemon-reload

Now the daemon is installed.

You can start, stop and restart the service using the following commands:

sudo systemctl start xslweb
sudo systemctl stop xslweb
sudo systemctl restart xslweb

16.2.2. macOS

XSLWeb can be installed as a daemon under macOS by adding the script run-service.sh to the Launch Daemon process of macOS. Execute the following steps:

  • Give the script file execute permissions by executing the following command (replace <install-dir>):

sudo chmod +x <install-dir>/bin/run-service.sh
  • Create the daemon property list file /Library/LaunchDaemons/com.xslweb.plist as root user, and give it the XML configuration below. Replace [install-dir] and [root-user] with values appropriate for your system. See https://www.launchd.info for more information on all possible configuration properties.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>EnvironmentVariables</key>
    <dict>
      <key>PATH</key>
      <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:</string>
    </dict>
    <key>Label</key>
    <string>com.xslweb</string>
    <key>Program</key>
    <string>[install-dir]/bin/run-service.sh</string>
    <key>WorkingDirectory</key>
    <string>[install-dir]/bin</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
    <key>LaunchOnlyOnce</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/startup.stdout</string>
    <key>StandardErrorPath</key>
    <string>/tmp/startup.stderr</string>
    <key>UserName</key>
    <string>[root-user]</string>
    <key>GroupName</key>
    <string>admin</string>
    <key>InitGroups</key>
    <true/>
  </dict>
</plist>
  • After you add the property list file onto the launchctl using the command below, the daemon is installed permanently:

sudo launchctl load -w /Library/LaunchDaemons/com.xslweb.plist
  • You can permanently remove the plist from the Launch Daemon by executing:

sudo launchctl load -w /Library/LaunchDaemons/com.xslweb.plist
  • and to stop the launchctl process:

sudo launchctl stop /Library/LaunchDaemons/com.xslweb.plist
  • and to start the launchctl process:

sudo launchctl start /Library/LaunchDaemons/com.xslweb.plist

16.2.3. Windows

The XSLWeb ready-to-run distribution is shipped with a copy of the Windows service manager NSSM that can be used to install, start, stop, restart and remove XSLWeb as a Window service using the corresponding batch files in the directory <install-dir>\bin. Once the service is installed it can also be managed from the standard Windows Services Manager application.

After a change to the variable MAXMEM in the file <install-dir>\bin\config.cmd the service must be removed and reinstalled.

16.3. Installation of the Web Application Archive (.war) distribution

This distribution contains the XSLWeb home directory and a web application archive (.war) and is most suitable to run XSLWeb in a production setting. The web application archive is a standard J2EE web application that can be installed on any Java application server that supports Servlet Spec 3.0 (Tomcat 7+, TomEE 1.6+, WebLogic, Jetty 8+, Glassfish 3+, JBoss AS 6.x/7.x etc.).

16.3.2. Install

Extract the archive to a directory of your choice. This directory will contain the file xslweb.war , a directory docs and a directory home. The installation of a war is application server specific, so please consult the manual of your server for that. Regardless of which application server is used, two settings are essential:

  1. The home directory of XSLWeb must be specified using a Java System Property called xslweb.home.

  2. The path «xslweb-home»/config must be added to the Java classpath.

16.3.3. Run

Start your application server. Open a web browser and go to the address:

where port is the port your application server runs on. A web page with the text “It works!” should appear. From here you can go to the examples and the documentation.

16.4. Run XSLWeb inside a Docker container

16.4.1. Requirements

Install Docker on your computer or server, see Get Docker.

16.4.2. Use prebuilt image from Docker Hub

A prebuilt image is available from Docker Hub. You can pull and run this image by following these steps:

  • Pull the image:

    docker pull armatiek/xslweb:4.2.0

  • Run the image:

    docker run -v {XSLWEB-HOME}:/usr/local/xslweb/home -p 8081:8080 --name xslweb armatiek/xslweb:4.2.0

    where {XSLWEB-HOME} must be changed to your XSLWeb home directory on your host machine and port 8080 of the container is bound to TCP port 80 on 127.0.0.1 of your host machine.

  • XSLWeb should now be accessible via http://localhost:8081

16.4.3. Build your own XSLWeb docker image

You can build your own custom XSLWeb docker image by following these steps:

  • Build the docker image by running build_docker.sh (Linux/macOS) or build_docker.bat (Windows). This runs the Dockerfile to build a local container image using Apache Tomcat version 10.0.14 and OpenJDK version 17.0.1+12.

  • Set the environment variable LOCAL_XSLWEB_HOME to point to your local XSLWeb home directory. This variable is referred to in the Docker Compose configuration file docker-compose.yml

  • Run docker-compose up and navigate your browser to http://localhost:8080. If you want to run it in the background, run docker-compose up -d.

  • In Dockerfile you can optionally change the environment variables PORT and CONTEXT_PATH, XSLWeb wil run on http://localhost:$PORT/$CONTEXT_PATH.

Some tips:

  • The command docker ps will show you the running containers, including their id’s

  • If you want to log into a container do this: docker exec -it xslweb bash

  • If you run in background, and want to tail the log, do this: docker logs xslweb --tail 1000 -f

17. Support for Saxon PE (Professional) and EE (Enterprise Edition)

The downloadable binaries of XSLWeb contain the open source Home Edition (HE) of the Saxon XSLT processor. You can build a version of XSLWeb that contains Saxon PE or EE by following these steps:

18. Appendix A: Request XML example

<?xml version="1.0" encoding="UTF-8"?>
<request xmlns="http://www.armatiek.com/xslweb/request">
  <character-encoding>UTF-8</character-encoding>
  <content-length>-1</content-length>
  <context-path>/xslweb</context-path>
  <local-addr>127.0.0.1</local-addr>
  <local-name>127.0.0.1</local-name>
  <local-port>8080</local-port>
  <method>GET</method>
  <path>/log/log.html</path>
  <path-info>/examples/log/log.html</path-info>
  <path-translated>D:\webapps\xslweb\examples\log\log.html</path-translated>
  <protocol>HTTP/1.1</protocol>
  <remote-addr>127.0.0.1</remote-addr>
  <remote-host>127.0.0.1</remote-host>
  <remote-port>55451</remote-port>
  <requested-session-id>D5984A4C38D09BE74C04F1D89022AE90</requested-session-id>
  <request-URI>/xslweb/examples/log/log.html</request-URI>
  <request-url>http://localhost:8080/xslweb/examples/log/log.html</request-url>
  <scheme>http</scheme>
  <server-name>localhost</server-name>
  <server-port>8080</server-port>
  <servlet-path/>
  <webapp-path>/examples</webapp-path>
  <is-secure>false</is-secure>
  <is-requested-session-id-from-cookie>true</is-requested-session-id-from-cookie>
  <is-requested-session-id-from-url>false</is-requested-session-id-from-url>
  <is-requested-session-id-valid>true</is-requested-session-id-valid>
  <headers>
    <header name="host">localhost:8080</header>
    <header name="connection">keep-alive</header>
    <header name="accept">text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8</header>
    <header name="user-agent">Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36</header>
    <header name="referer">http://localhost:8080/xslweb/examples</header>
    <header name="accept-encoding">gzip, deflate, sdch</header>
    <header name="accept-language">nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4</header>
    <header name="cookie">JSESSIONID=D5984A4C38D09BE74C04F1D89022AE90</header>
  </headers>
  <parameters>
    <parameter name="country">
      <value>US</value>
    </parameter>
    <parameter name="states">
      <value>AZ</value>
      <value>CA</value>
    </parameter>
  </parameters>
  <!-- If this request was a file upload POST request:
  <file-uploads>
    <file-upload>
      <file-path>C:\Users\John\AppData\Local\Temp\48226ce5-7bba-4986-8d1f-c4a8f34638cf\MyDocument1.docx</file-path>
      <field-name>file1</field-name>
      <file-name>MyDocument1.docx</file-name>
      <content-type>application/vnd.openxmlformats-officedocument.wordprocessingml.document</content-type>
      <size>177032</size>
    </file-upload>
  </file-uploads>
  -->
  <session>
    <creation-time>2015-01-06T13:06:04.925+01:00</creation-time>
    <id>D5984A4C38D09BE74C04F1D89022AE90</id>
    <last-accessed-time>2015-01-06T14:36:04.909+01:00</last-accessed-time>
    <max-inactive-interval>1800</max-inactive-interval>
    <is-new>false</is-new>
  </session>
  <cookies>
    <cookie>
      <max-age>-1</max-age>
      <name>JSESSIONID</name>
      <is-secure>false</is-secure>
      <value>D5984A4C38D09BE74C04F1D89022AE90</value>
      <version>0</version>
    </cookie>
  </cookies>
</request>

19. Appendix B: Response XML example

<?xml version="1.0" encoding="UTF-8"?>
<resp:response
  xmlns:resp="http://www.armatiek.com/xslweb/response"
  status="200">
  <resp:headers>
    <resp:header name="Pragma">no-cache</resp:header>
    <resp:int-header name="Expires">0</resp:int-header>
    <resp:date-header
      name="Last-Modified">2006-04-10T13:40:23.83-05:00</resp:date-header>
  </resp:headers>
  <resp:session max-active-interval="1800">
    <resp:attributes>
      <resp:attribute name="msg">
        <item type="xs:string">Hello World</item>
        <item type="node()">
          <msg>Hello World!</msg>
        </item>
      </resp:attribute>
    </resp:attributes>
  </resp:session>
  <resp:cookies>
    <resp:cookie>
      <resp:comment>Comment 1</resp:comment>
      <resp:domain>localhost.com</resp:domain>
      <resp:max-age>-1</resp:max-age>
      <resp:name>cookie-1</resp:name>
      <resp:path>/examples</resp:path>
      <resp:is-secure>false</resp:is-secure>
      <resp:value>cookie-1-value</resp:value>
      <resp:version>0</resp:version>
    </resp:cookie>
  </resp:cookies>
  <resp:body>
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Hello World!</title>
      </head>
      <body>
        <h1>Hello World</h1>
      </body>
    </html>
  </resp:body>
</resp:response>

20. Appendix C: Webapp XML example

<?xml version="1.0" encoding="UTF-8"?>
<webapp
  xmlns="http://www.armatiek.com/xslweb/webapp"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.armatiek.com/xslweb/webapp
../../config/xsd/xslweb/webapp.xsd">

  <title>XSLWeb examples</title>
  <description>XSLWeb examples</description>
  <development-mode>true</development-mode>

  <!-- Resources to serve straight away: -->
  <resources>
    <resource pattern="/favicon.ico" media-type="image/x-icon"/>
    <resource
      pattern="/(styles|images)/.+\.png"
      media-type="image/png"
      duration="P7DT0H0M0S"/>
    <resource pattern="/(styles|images)/.+\.gif" media-type="image/gif"/>
    <resource pattern="/(styles|images)/.+\.(jpg|jpeg)" media-type="image/jpg"/>
    <resource pattern="/scripts/.+\.js" media-type="text/javascript"/>
    <resource pattern="/styles/.+\.css" media-type="text/css"/>
    <resource pattern="/downloads/.+\.docx?" media-type="application/msword"/>
  </resources>

  <!-- Stylesheet parameters: -->
  <parameters>
    <parameter
      name="hostname"
      uri="http://www.armatiek.com/xslweb/functions/email"
      type="xs:string">
      <value>smtp.googlemail.com</value>
    </parameter>
    <parameter
      name="port"
      uri="http://www.armatiek.com/xslweb/functions/email"
      type="xs:integer">
      <value>465</value>
    </parameter>
    <parameter
      name="username"
      uri="http://www.armatiek.com/xslweb/functions/email"
      type="xs:string">
      <value>MYUSERNAME</value>
    </parameter>
    <parameter
      name="password"
      uri="http://www.armatiek.com/xslweb/functions/email"
      type="xs:string">
      <value>MYPASSWORD</value>
    </parameter>
    <parameter
      name="use-ssl"
      uri="http://www.armatiek.com/xslweb/functions/email"
      type="xs:boolean">
      <value>true</value>
    </parameter>
  </parameters>

  <!-- Scheduled job definitions: -->
  <jobs>
    <job>
      <name>WriteTimeJob</name>
      <uri>execute-writetime-job.html</uri>
      <!-- Execute every 60 seconds: -->
      <cron>0/60 * * * * ?</cron>
      <concurrent>true</concurrent>
    </job>
  </jobs>
  <datasources>
    <datasource>
      <name>datasource-worldcup</name>
      <driver-class>org.h2.Driver</driver-class>
      <jdbc-url>jdbc:h2:file://${webapp-dir}/xsl/relational-database/worldcup.mv</jdbc-url>
      <property name="user">sa</property>
    </datasource>
  </datasources>

</webapp>