Communicate between stylesheets using WebSphere DataPower context variables

Websphere® DataPower SOA 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.

Share:

David Z. Maze (dmaze@us.ibm.com), Software Engineer, IBM

David Maze photoDavid Maze is an engineer in the IBM WebSphere DataPower XML Technology group. His major DataPower projects have included rewriting the XML Schema validation engine, software integration for the XG4 XML accelerator, and work on the DataPower rule execution engine.



15 August 2007

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: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

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 <xsl:stylesheet> 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 dp:variable().

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/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

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 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.

Figure 1. Processing policy configuration invoking "get SOAP header" stylesheet
Figure 1. Processing policy configuration invoking

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
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 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.

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 var://context/stylesheet/name.

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 <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

XSLT variables are immutable: once a variable gets a value via <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.

EXSLT user extension functions

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 <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.

To address the need to provide a mutable state in a single stylesheet, the DataPower appliance provides similar <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.

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 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.

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>

Summary

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.


Download

DescriptionNameSize
DataPower domain export with dp:variable sampledpvar-ssonly.zip2KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=248184
ArticleTitle=Communicate between stylesheets using WebSphere DataPower context variables
publish-date=08152007