<EDIT>you can play with DataPower developer Edition for free if you are interested: https://developer.ibm.com/datapower/docker/</EDIT>
In 2015 I blogged on DataPower Languages, and how to call from one of DataPower languages out to others (GatewayScript, XSLT, XQuery, XPath, Schema).
In 2016 I blogged on Using XPath 1.0 and XPath 2.0 in GatewayScript, which demonstrated how GatewayScript called out to XSLT that called out to XQuery allowing XPath 2.0 evaluation on XML in GatewayScript not available otherwise. In the solution there were three static files: GatewayScript script, XSLT stylesheet and XQuery script.
Last August I answered developerWorks forum posting Dynamic XQuery on how to execute dynamic XQuery from XSLT.
Today I want to show a more dynamic approach of calling XQuery from GatewayScript through XSLT, the red path in this diagram:
The simple XQuery sample I use today just calls (Xquery) XPaths 2.0 function fn:reverse() on XML nodeset "//*" from GatewayScript:
💻 coproc2 3lang.js <(echo '<a>1<b>2</b>3</a>') http://dp6-l3:2227; echo
<b>2</b><a>1<b>2</b>3</a>
💻
You can find complete 3-language script here:
https://stamm-wilbrandt.de/en/blog/3lang.js
So lets discuss the different parts here now. Here GatewayScript variable "xslt" gets defined as XML parsed stylesheet. Why parsing? Sometimes in XSLT both, quotes and apostrophes are needed, which requires escaping in a GatewayScript string. Parsing successfully assures that no typo is in the stylesheet part. As you can see the XQuery script is inside the $xq XSLT variable definition. I added an unnneded XQuery comment only to have comment for 3 languages in a single source file (there is an XML comment explaining $xq nd the initial GatewayScript comment as well. While it is not documented in Knowledge center how to write to a context with <dp:set-variable> in XSLT, it works and writing to context variables is described of course. As you can see you need to write a sting into the context, writing just $xq (which is a text node) would not work. Finally <dp:xquery-transform> executed the dynamically create XQuery script in context "xquery":
// GatewayScript executes stylesheet "xslt", which executes XQuery script $xq
//
var xslt = XML.parse(" \
<xsl:stylesheet version='1.0' \
xmlns:xsl='http://www.w3.org/1999/XSL/Transform' \
xmlns:dp='http://www.datapower.com/extensions' \
extension-element-prefixes='dp' \
> \
<xsl:template match='/'> \
<!-- $xq is XQuery script to be executed in XSLT --> \
<xsl:variable name='xq'>fn:reverse(//*) (: comment :)</xsl:variable> \
\
<dp:set-variable name=\"'var://context/xquery'\" value='string($xq)' /> \
\
<xsl:copy-of select=\"dp:xquery-transform('var://context/xquery', /)\"/> \
</xsl:template> \
\
</xsl:stylesheet> \
"); // A
Next the input XML gets parsed in GatewayScript via .readAsXML(), the input "file" gets created on the fly in a bash sub shell in coproc2 command above:
var fs = require('fs');
var transform = require('transform');
session.input.readAsXML(function(error, xml) { throwIf(error); // B
While GatewayScript context object allows to execute scripts stored in a context, and to create contexts, read and write to/from context variables, it misses the feature of filling a context with content. So how can we pass a dynamic XSLt for GatewayScript execution? The answer is to generate a temp file in "temporary:" folder, which is a ramdisk filesystem. In order to avoid any interference between concurrently executed transactions, fs module fs.temporary() function creates a unique temporary filename each time. I never used GatewayScript temporary files until now, but I really like the "Mission Impossible" like "this tempfile will self destruct in 5 seconds" ;-) So the fs.writeFile() just stores our XSLT in a temporary file, that will go away from alone in 5 seconds:
var tmp_file = fs.temporary();
var options = {'file': tmp_file, 'data': XML.stringify(xslt), 'TTL': 5};
fs.writeFile(options, function(error) { throwIf(error); // C
Here we all out to XSLT referencing the new temp file as stylsheet:
options = {"location": tmp_file, "xmldom": xml};
transform.xslt(options, function(err, nodelist, abort) { throwIf(err); // D
Finally we output the resulting XML nodeset to session.output in GatewayScript. The explicit call to XML.stringify() demonstrates how to pass the option corresponding to XSLT's <xsl:output> setting omit-xml-declaration="yes":
session.output.write(
XML.stringify( {omitXmlDeclaration: true}, nodelist) // E
);
The rest is just my minimal error handling throwIf() function, which minimizes code lines in above code. It behaves identical to XSLT <xsl:message terminate="yes">, aborts immediately (as long as no catch block is specified in GatewayScript):
});
});
});
function throwIf(error) { if (error) throw error; } // F
Have fun with dynamic scripts in GatewayScript/XSLT, and self destruction temp files,
Hermann.