Communicate between stylesheets using WebSphere DataPower context variables
About DataPower XML appliances
You can use WebSphere DataPower SOA 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
<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
Using dp:variable() and <dp:set-variable/> extensions
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,
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
element at the topmost level of the stylesheet.
In addition to the extensions described here, given a fixed variable name and a fixed string value, a
setvar action in the processing policy
will set the variable to the value. It can then be accessed in later stylesheets using
Contexts, context variables, and system variables
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
var:. There are four variable syntaxes:
var://context/contextname/varnamealways refers to a variable called
varnamein the context named
contextname. As a special case,
var://context/contextname/_roottreerefers to the actual data stored in the context, not a specific variable within it.
var://local/varnamerefers 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/varnamerefers 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/propertyrefers 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
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:
Listing 1. Extracting a SOAP header
<?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>
Note that the
dp: XML namespace prefix is declared, and referenced as both an extension element prefix -- explicit
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
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
variable with the SOAP header we're interested in. This policy is shown in Figure 1.
Figure 1. Processing policy configuration invoking "get SOAP header" stylesheet
You can use the
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.
Listing 2. Inserting a saved SOAP header
<?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>
This stylesheet refers to the "saved" context from the request rule explicitly, so it might be configured to take
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.
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".
Figure 2. DataPower XI50 probe with context variables
Dynamically generated stylesheets and dynamically selected inputs
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
for example, looks at the
input variable in the
context context. If that variable is set to
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.
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
Listing 3. Picking a stylesheet based on SOAP version
<?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>
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
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.
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
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
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
XSLT variables are immutable: once a variable gets a value via
<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.
DataPower users have used the context-variable mechanism to provide mutable state within a single stylesheet. Calling
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.
To address the need to provide a mutable state in a single stylesheet, the DataPower appliance provides similar
dp:local-variable() extensions. These work the same way as
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.
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
kvp (key-value pair) element sets a local variable whose name is derived from a
element, and sets it to the children of a
value element, while the
tns:lookup() function gets the value corresponding
to a named key.
Listing 4. Using local variables for a lookup table
<?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>
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.
- DataPower Extension Elements/Functions Catalog
- Enable XML awareness in WebSphere Extended Deployment with WebSphere DataPower SOA Appliances
- Web services security with WebSphere Application Server V6, Part 5: DataPower as a gateway device
- Bill Hines: The (XML) threat is out there