White Papers
Abstract
IBM DataPower Gateway Appliances provide a high-speed engine for transforming XML using XSLT 1.0. A processing policy can run multiple stylesheets over a given input, or perform processing on messages passing through the device in both directions. This article discusses the dp:variable XSLT extension, which allows values to be shared between stylesheet executions in the same transaction.
There is a section at the end for downloadable content that includes a PDF version of this document and a zip file for an example on DP variables.
Content
You can use IBM DataPower Gateway Appliances for XML to accelerate filtering, transformation, and securing of XML data flows. A basic configuration might check schema validity, digital signatures, and other attributes of inbound messages, then pass it on to a fixed back-end. More complicated rules might decrypt inbound messages, dynamically select a back-end based on XML content, transform the data to a different format, and so on. In custom XSLT, DataPower extensions also make available information about an inbound connection''s IP address, port number, and such for logging or inserting into outbound messages. One powerful feature of the DataPower engine is the use of context variables to communicate information across processing steps, possibly even across different rules in the same transaction. Fpr example, you could configure a policy to capture a SOAP header on a request rule and insert that same header into the response to the original client. You could use the same context variable mechanism to capture the complete result of an earlier processing step in a custom stylesheet, or to access the transaction metadata mentioned previously. This article will discuss how to use the The DataPower Extension Elements/Functions Catalog lists all of the XSLT extension elements and functions available to stylesheets run on the appliance. Among these extensions are an extension function, In addition to the extensions described here, given a fixed variable name and a fixed string value, a In the DataPower processing policy configuration, every step has a labeled input and output. The names provided in the configuration are actually the names of contexts in the system. A context stores not only data, such as an XML tree that is the input or output of a transformation, but also a set of context variables. There are a small set of special contexts, such as INPUT and OUTPUT to refer to the current rule's input and output data, and PIPE for streaming transformations. Otherwise, the set of contexts persists across an entire transaction, including the request and response rules and any called or error rule that might be invoked. Every variable has a name, which is really a URL beginning with Let's copy a SOAP header from an input message into the response. First we need to extract the SOAP header we're interested in, and use <dp:set-variable/> to save it to a context variable. The following stylesheet can do that: Note that the You can use the This stylesheet refers to the "saved" context from the request rule explicitly, so it might be configured to take As a debugging aid, the DataPower probe can be used to look at the saved variables. Figure 2 shows how the probe presents the list of variables after sending a sample XML file through the device with this policy. Because the context variables can be accessed from any other stylesheet, the probe considers these "global". In most places where context names or target URLs appear in a processing policy configuration, a context variable name can be used instead. If the context variable contains a string, the value of the variable is interpreted as the name of the thing. Setting a processing step's input to Listing 3 shows how this technique can be applied to select a different stylesheet depending on the SOAP version of the input message. The stylesheet looks at the input message, and chooses either a stylesheet suited to handling SOAP 1.1 or 1.2 messages based on the name of its root element. A processing policy is then configured to run first this stylesheet, and then the stylesheet named by You could also use context variables to name a schema for a validate action if a particular schema variant is only known through something like a One other interesting set-up is providing a context name, rather than a URL, as the stylesheet name in a transform action. In this case, the contents of that context are used as a stylesheet, rather than fetching a fixed stylesheet. You might use this to fill in XPath expressions in the stylesheet for XSLT variables are immutable: once a variable gets a value via DataPower appliances support most of the EXSLT extension functions. Of note, they support the func:function extension to create user-defined extension functions. These functions act like XSLT named templates, except that they're called using the XPath function call syntax, and they can return any XPath type, whereas named templates can return only XSLT result tree fragments. DataPower users have used the context-variable mechanism to provide mutable state within a single stylesheet. Calling To address the need to provide a mutable state in a single stylesheet, the DataPower appliance provides similar One interesting use for local variables is to implement a table mapping some key to some value, where the value can be updated later in the same stylesheet. Note that we've used XPath string syntax in all of the examples so far. You can actually use any XPath expression resolving to a string as a variable name. Listing 4 demonstrates this technique: matching a DataPower extensions provide powerful features for communicating between stylesheets. A value or XML tree can be computed in one stylesheet, saved, and used in another stylesheet, possibly on the return path in the same transaction. You can also use context variables to dynamically select a stylesheet, or use the output of one step as the stylesheet of the next.About DataPower XML appliances
dp:variable() and <dp:set-variable/> extensions to access context and service variables. You can also use context variables to control which stylesheet is executed, which rule is called, or which input is used for a future processing step. If the state needs to be used only within a single stylesheet, DataPower appliances also provide stylesheet-local variables that can be easier to use than passing around XSLT template parameters.Using dp:variable() and <dp:set-variable/> extensions
dp:variable(), and an extension element, <dp:set-variable/>. To use these extensions, a stylesheet needs to tell the XSLT engine about the dp: XML namespace, and that that namespace contains extension elements. It's also common to also ask the processor not to include that namespace in the stylesheet's output. This set-up occurs in the <xsl:stylesheet> element at the topmost level of the stylesheet.setvar action in the processing policy will set the variable to the value. It can then be accessed in later stylesheets using dp:variable().Contexts, context variables, and system variables
var:. There are four variable syntaxes:
var://context/contextname/varname always refers to a variable called varname in the context named contextname. As a special case, var://context/contextname/_roottree refers to the actual data stored in the context, not a specific variable within it.var://local/varname refers to a variable called varname. If setting a variable, it is set in the output context of the current stylesheet; if reading one, it is read from the input context.var://system/contextname/varname refers to a global variable. Global variables can be used in much the same way as context variables, but their values persist beyond the current transaction. Global variables are difficult to use safely because there is no way to protect a variable from being updated in separate concurrent transactions or to have a variable's value persist across device restarts.var://service/property refers to a service variable. You can find a full listing of service variables in Appendix A of the Extension Elements/Functions Catalog; reading var://service/protocol, for example, retrieves the protocol ("http", "https", and so on) for the current request.Using the extensions
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://www.example.org/tns" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp"> <xsl:output method="xml"/> <xsl:template match="/"> <dp:set-variable name="'var://local/header'" value="/soap:Envelope/soap:Header/tns:MyHeader"/> <xsl:copy-of select="/"/> </xsl:template> </xsl:stylesheet>
dp: XML namespace prefix is declared, and referenced as both an extension element prefix -- explicit dp: elements should be processed as extensions, not copied to the output -- and that the prefix should be excluded from the stylesheet's result. We set a variable named header on whatever our output context is containing an XSLT node-set with the header we're interested in, and then also copy the entire contents of our input into that context. If a transform action is configured with an input context of INPUT and an output context of saved, then the saved context will contain the input tree, plus an additional header variable with the SOAP header we're interested in. This policy is shown in Figure 1.
dp:variable() extension function to retrieve the contents of the variable in the response rule. This is called like any other XPath function. Because we expect its value to be a node-set, we'll call it from an <xsl:copy-of/> statement to insert its value into the result, as shown in Listing 2. <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://www.example.org/tns" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp"> <xsl:output method="xml"/> <xsl:template match="/"> <soap:Envelope> <soap:Header> <xsl:copy-of select="/soap:Envelope/soap:Header/*"/> <xsl:copy-of select="dp:variable('var://context/saved/header')"/> </soap:Header> <xsl:copy-of select="/soap:Envelope/soap:Body"/> </soap:Envelope> </xsl:template> </xsl:stylesheet> INPUT as its input and OUTPUT as its output. Since this is running as the response rule, it reads its input from the back-side service and writes its output as the response to the initial front-side connection.
Dynamically generated stylesheets and dynamically selected inputs
var://context/context/input, for example, looks at the input variable in the context context. If that variable is set to ctx1, the step's input is taken from the ctx1 variable. A more typical use would be to set a context variable to hold the URL of a stylesheet, and then use the context variable name as the stylesheet name in a transform action to dynamically select a stylesheet to run.var://context/stylesheet/name. <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" xmlns:tns="http://www.example.org/tns" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp"> <xsl:output method="xml"/> <xsl:template match="/"> <xsl:apply-templates mode="pick-stylesheet"/> <xsl:copy-of select="/"/> </xsl:template> <xsl:template match="/soap11:Envelope" mode="pick-stylesheet"> <dp:set-variable name="'var://context/stylesheet/name'" value="'local:///soap-1.1.xsl'"/> </xsl:template> <xsl:template match="/soap12:Envelope" mode="pick-stylesheet"> <dp:set-variable name="'var://context/stylesheet/name'" value="'local:///soap-1.2.xsl'"/> </xsl:template> <xsl:template match="*" mode="pick-stylesheet"> <xsl:message terminate="yes">Unrecognized SOAP envelope</xsl:message> </xsl:template> </xsl:stylesheet>
version attribute on the document root element. Using a context variable as the target rule name for a call action lets you call a rule dynamically based on something in the input.<xsl:key/> based on an unknown input, or otherwise set a fixed element name in an actual stylesheet from the input. Note that you need to use the <xsl:namespace-alias/> directive to generate XSLT from XSLT. For more information, see section 7.1.1 of the XSLT 1.0 specification.Using dp:local-variable() and <dp:set-local-variable/> extensions
<xsl:variable/> or <xsl:param/>, it cannot be changed. For a value to be computed in one template to be used in another, it must be passed along via a long sequence of parameters in template invocations.<dp:set-variable/> multiple times on the same variable name causes that variable's value to be overwritten, so stylesheets have been written that use context variables to save a value for use elsewhere within the same stylesheet without using XSLT parameters. However, the bookkeeping required to track these context variables can be significant, and any variables set this way are persistent until the end of the transaction, which may be after the response rule has run.<dp:set-local-variable/> and dp:local-variable() extensions. These work the same way as <dp:set-variable/> and dp:variable(), but with two differences: local variables always refer to a value specific to the current stylesheet execution, and they may have any string as a name.kvp (key-value pair) element sets a local variable whose name is derived from a key child element, and sets it to the children of a value element, while the tns:lookup() function gets the value corresponding to a named key. <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tns="http://www.example.org/tns" xmlns:dp="http://www.datapower.com/extensions" xmlns:func="http://exslt.org/functions" extension-element-prefixes="dp func" exclude-result-prefixes="dp func"> ... <xsl:template match="tns:kvp"> <dp:set-local-variable name="concat('key:',tns:key)" value="tns:value/*"/> </xsl:template> <func:function name="tns:lookup"> <xsl:param name="key" select="''"/> <func:result select="dp:local-variable(concat('key:',$key))"/> </func:function> ... </xsl:stylesheet> Summary