You are on page 1of 44

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.
13 Comments
Share:
Facebook
Twitter
Linked In
Google+
David Z. Maze (dmaze@us.ibm.com), Software Engineer, IBM
15 August 2007
Try IBM WebSphere DataPower XC10 Virtual Appliance for Developers

Table of contents

Develop and deploy your next


app on the IBM Bluemix
cloud platform.
Start building for free

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

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.
var://context/contextname/varname

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

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
tovar://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 byvar://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 aversion 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:namespacealias/> 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 EXSLTextension functions. Of note, they support
thefunc: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.

Develop and deploy your next


app on the IBM Bluemix
cloud platform.

Start building for free

Introduction

In the world of Web services, Web Services Description Language (WSDL) documents
describe the interfaces provided by Web services -- and indicate where instances of
those Web services can be found on the network. In general, WSDL documents are
composed and consumed with the aid of various development tools. As with virtually all
Web service related information, WSDL documents are expressed as standard XML
documents.
One of the strengths of XML is the availability of increasingly powerful tools to process
the data. One such tool, XSLT, can be used to effect complex transformations on XML
data. Since WSDL documents are expressed as XML, it is possible to automate a wide
variety of WSDL conversion tasks using XSLT. This article describes some of the
approaches that have proven useful in processing WSDL documents with XSLT (and
some that haven't) and lists some general techniques that can be applied to simplify
automated processing of WSDL documents.
This document assumes a working knowledge of both WSDL and XSLT. The following
links on IBM developerWorks offer useful background information:

For background on Web services standards such as WSDL,


visit Standards and Web services.

For more information on WSDL, refer to the article, New to SOA and
Web services.

For more information on XSLT, refer to New to XML.


Back to top
SAX and DOM versus XSL

There are two basic approaches to modifying a WSDL document. The first uses a
language such a Java or C and an XML-parsing API such as SAX or DOM. The original
WSDL document is read, its contents parsed and modified by the algorithm and the
modified WSDL document constructed -- either by manually assembling the document
out of strings or by using a language-specific XML library. The second approach utilizes
an XSL transform that specifies how the input WSDL document should be manipulated
to produce the output WSDL document. While many of the techniques described here
apply to the first approach, the XML approach has several advantages:

1.

General-purpose languages have no special constructs for dealing


with XML documents. While it is certainly possible to use them for such
tasks, it requires much more effort to achieve equivalent results.
2.
With a general-purpose language, the concepts behind the
transformation get lost in the sheer volume of code necessary to achieve it.
The relative terseness of the XSL transform makes the underlying concepts
more apparent and thus easier to debug and maintain.
3.
XSL transforms can be processed by a number of different tools,
many of which are already widely available. For example, the ant build tool,
the libxslt package available on most Linux distributions, the Java runtime
environment and many Web browsers can execute XSL transforms.
For completeness, it should be noted that XSL is somewhat weak in its ability to express
algorithms that are not directly related to the structure of the input XML document. If the
needed transformations make heavy use of such algorithms, a general-purpose
language is likely to be a better choice than XSL.
Back to top
Challenges of WSDL

Extracting structure from a WSDL document using XSL results in some interesting
challenges. To allow use of the widest possible set of XSL transform engines, restrict
stylesheets to features defined in XSL version 1.1. Some of the features added in XSL
version 2.0 will simplify some of the XSL shown here, but will not be prevalent in many
environments for some time.
The largest obstacle in processing WSDL documents with XSL is the extensive use of
namespace prefixes within attribute values in WSDL documents. These namespace
prefixes are used to correlate messages, portTypes, bindings and ports. While XSL is
adept at handling namespaces on both elements and attributes in the input document,
its constructs for dealing with them inside attribute values and character data are
somewhat weak.
Back to top
Relationships within a WSDL document

While it is not possible to fully describe the structure of a WSDL document here, it is
worth describing a few of the salient relationships among elements of the WSDL
document. Correct manipulation of the WSDL document depends on understanding and
preserving these relationships.
Figure 1. Relationship of WSDL document elements

Each <wsdl:message>, <wsdl:portType> and <wsdl:binding> element contains a


required "name" attribute of type ncname, but is referenced from elsewhere in the
WSDL document using a qname. The URIs associated with the qnames are inherited
from thetargetNamespace attribute on the <wsdl:definitions> element. If
the <wsdl:definitions> element does not specify a targetNamespace, the qname inherits
a null namespace URI. This implicit namespace qualification is shown by dashed gray
arrow in figure 1.
The following additional "interesting" relationships exist among the elements in a WSDL
document. Each is shown with a solid black arrow in figure 1.

Within the <wsdl:operation>s defined in a <wsdl:portType>,


each <wsdl:input>, <wsdl:output> and <wsdl:fault> element refers to
a <wsdl:message> via its message attribute.That is:

wsdl:portType/wsdl:operation/wsdl:input/@message -> wsdl:message

wsdl:portType/wsdl:operation/wsdl:output/@message -> wsdl:message

wsdl:portType/wsdl:operation/wsdl:fault/@message -> wsdl:message

The "type" attribute on a <wsdl:binding> element refers to


a <wsdl:portType> element. That is:
wsdl:binding/@type -> wsdl:portType

Within a <wsdl:binding> element, each <wsdl:operation> has a "name"


attribute that equals the "name" attribute on a <wsdl:operation> in the
associated <wsdl:portType>.
wsdl:binding/wsdl:operation/@name = wsdl:portType/wsdl:operation/@name

Within the <wsdl:operation>s defined in a <wsdl:binding>,


each <wsdl:input>, <wsdl:output> and <wsdl:fault> element may contain
a name attribute whose value matches a name attribute on the corresponding
element in the <wsdl:portType>. That is:

wsdl:binding/wsdl:operation/wsdl:input/@name =

wsdl:portType/wsdl:operation/wsdl:input/@name

wsdl:binding/wsdl:operation/wsdl:output/@name =

wsdl:portType/wsdl:operation/wsdl:output/@name

wsdl:binding/wsdl:operation/wsdl:fault/@name =

wsdl:portType/wsdl:operation/wsdl:fault/@name

Back to top
Techniques

What follows are several techniques and XSL snippets that have proven useful in
processing WSDL documents. Many of these approaches could be generalized to work
with non-WSDL XML documents as well.
In addition to an XSL snippet, each technique described below includes a simple
example stylesheet showing how the technique can be used to process a WSDL
document. For each example stylesheet, the output of applying the stylesheet to the
following contrived WSDL document is also shown:
Listing 1. test.wsdl used in examples

<?xml version="1.0" encoding="UTF-8"?>


<!-This file contains an imaginary WSDL used to illustrate how various
types of XSL transformations can be applied to it.
-->
<wsdl:definitions name="test"
targetNamespace="http://tempuri.org/test/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:test="http://tempuri.org/test/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
>
<wsdl:message name="inputmsg1">
<wsdl:part name="inputmsg1-part" type="xsd:string"/>
</wsdl:message>
<wsdl:message name="inputmsg2">
<wsdl:part name="inputmsg2-part" type="xsd:decimal"/>
</wsdl:message>
<wsdl:message name="inputmsg3">
<wsdl:part name="inputmsg3-part" type="xsd:integer"/>
</wsdl:message>
<wsdl:message name="outputmsg">
<wsdl:part name="outputmsg-part" type="xsd:base64Binary"/>
</wsdl:message>

<wsdl:message name="faultmsg"/>

<wsdl:portType name="porttype">
<!-- one-way operation -->
<wsdl:operation name="porttype-op1">
<wsdl:input message="test:inputmsg1" name="porttype-op1-input"/>
</wsdl:operation>
<!-- request-response operation -->
<wsdl:operation name="porttype-op2">
<wsdl:input message="test:inputmsg2" name="porttype-op2-input"/>
<wsdl:output message="test:outputmsg" name="porttype-op2-output"/>
<wsdl:fault message="test:faultmsg" name="porttype-op2-fault"/>
</wsdl:operation>
<!-- solicit-response operation -->
<wsdl:operation name="porttype-op3">
<wsdl:output message="test:outputmsg" name="porttype-op3-output"/>
<wsdl:input message="test:inputmsg3" name="porttype-op3-input"/>
<wsdl:fault message="test:faultmsg" name="porttype-op3-fault"/>
</wsdl:operation>
<!-- notification operation -->
<wsdl:operation name="porttype-op4">
<wsdl:output message="test:outputmsg" name="porttype-op4-output"/>
</wsdl:operation>
</wsdl:portType>

<wsdl:binding name="binding" type="test:porttype">


<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="porttype-op1">
<soap:operation soapAction="http://tempuri.org/test/soapAction"/>
<wsdl:input name="porttype-op1-input">
<soap:body use="literal" parts="inputmsg1-part"/>
</wsdl:input>
</wsdl:operation>
<wsdl:operation name="porttype-op2">
<soap:operation soapAction="http://tempuri.org/test/soapAction"/>
<wsdl:input name="porttype-op2-input">
<soap:body use="literal" parts="inputmsg2-part"/>
</wsdl:input>
<wsdl:output name="porttype-op2-output">
<soap:body use="literal" parts="outputmsg-part"/>
</wsdl:output>
<wsdl:fault name="porttype-op2-fault">
<soap:fault name="porttype-op2-fault" use="literal"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="porttype-op3">
<soap:operation soapAction="http://tempuri.org/test/soapAction"/>
<wsdl:input name="porttype-op3-input">
<soap:body use="literal" parts="inputmsg3-part"/>
</wsdl:input>
<wsdl:output name="porttype-op3-output">
<soap:body use="literal" parts="outputmsg-part"/>
</wsdl:output>

<wsdl:fault name="porttype-op3-fault">
<soap:fault name="porttype-op3-fault" use="literal"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="porttype-op4">
<soap:operation soapAction="http://tempuri.org/test/soapAction"/>
<wsdl:output name="porttype-op4-output">
<soap:body use="literal" parts="outputmsg-part"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>

<wsdl:service name="service">
<wsdl:port name="service-port" binding="test:binding">
<soap:address location="http://localhost:9080/not/used"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

Technique 1: Map a namespace prefix to a namespace URI

Many WSDL elements define attributes of type qname. Since the mapping of
namespace prefixes to URIs is not required to be unique or consistent even within a
single document, any robust WSDL parsing logic must be able to translate the
namespace prefixes in qnames to namespace URIs. The XSL namespace-uri() function
maps a namespace prefix to a namespace URI, but only understands nodes in the input
document. Since the namespace prefix is part of an attribute value in the WSDL case,
some additional work is required.
The following XSL code translates the namespace prefix in a string to the corresponding
namespace URI. If the supplied string does not contain a prefix, the namespace URI is
an empty string. Placing this logic in its own template allows it to be easily invoked from
anywhere, in the context of arbitrary elements.
Listing 2. Technique 1 XSL
<!-- emits the namespace uri associated with the prefix of the
specified qname based on current()'s namespace nodes -->
<xsl:template name="namespace-uri-of-qname">
<xsl:param name="qname"/>
<xsl:if test="contains($qname,':')">
<xsl:value-of select="namespace::*[name()=substring-before($qname,':')]"/>
</xsl:if>
</xsl:template>

Here is an example of how this XSL template is used:


Listing 3. Technique 1 example
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="wsdl-util.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<!-- print out the namespace URIs associated with input messages -->
<xsl:template match="wsdl:input[@message]">

<xsl:text>prefix of </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text> maps to namespace uri </xsl:text>
<xsl:call-template name="namespace-uri-of-qname">
<xsl:with-param name="qname" select="name()"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
<xsl:text>prefix of </xsl:text>
<xsl:value-of select="@message"/>
<xsl:text> maps to namespace uri </xsl:text>
<xsl:call-template name="namespace-uri-of-qname">
<xsl:with-param name="qname" select="@message"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Running the above example against the test WSDL document produces:
Listing 4. Technique 1 example output
prefix
prefix
prefix
prefix
prefix
prefix

of
of
of
of
of
of

wsdl:input maps to namespace uri http://schemas.xmlsoap.org/wsdl/


test:inputmsg1 maps to namespace uri http://tempuri.org/test/
wsdl:input maps to namespace uri http://schemas.xmlsoap.org/wsdl/
test:inputmsg2 maps to namespace uri http://tempuri.org/test/
wsdl:input maps to namespace uri http://schemas.xmlsoap.org/wsdl/
test:inputmsg3 maps to namespace uri http://tempuri.org/test/

Technique 2: Map a namespace URI to a namespace prefix

The reverse problem exists when an XSL stylesheet needs to generate qnames in
WSDL document attributes. That is, the XSL stylesheet knows the namespace URI, but
needs to emit a namespace prefix that is mapped to that URI for the current element.
The XSL transform engine would handle this automatically if the namespace were
associated with an output element, but since WSDL requires it to be in an attribute
value, manual processing is required. Error handling makes the XSL for this technique a
bit more complex than for the first technique. Also shown in the following XSL is a
special case template that emits a prefix for the WSDL
document's targetNamespace URI.
Listing 5. Technique 2 XSL
<!-- emits a prefix that maps to the specified namespace uri for current() -->
<xsl:template name="prefix-for-namespace">
<xsl:param name="namespace-uri"/>
<xsl:if test="$namespace-uri">
<!-- terminate if current() does not have a prefix for $namespace-uri -->
<xsl:if test="not(namespace::*[string() = $namespace-uri])">
<xsl:message terminate="yes">
<xsl:text>Unable to find namespace prefix for namespace </xsl:text>
<xsl:value-of select="$namespace-uri"/>
<xsl:text> while processing </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text> element.</xsl:text>
</xsl:message>
</xsl:if>
<xsl:value-of select="name(namespace::*[string() = $namespace-uri])"/>
<xsl:text>:</xsl:text>
</xsl:if>

</xsl:template>

<!-- emits a prefix that maps to the wsdl:definitions/@targetNamespace -->


<xsl:template name="prefix-for-target-namespace">
<xsl:if test="/wsdl:definitions/@targetNamespace">
<xsl:call-template name="prefix-for-namespace">
<xsl:with-param name="namespace-uri">
<xsl:value-of select="/wsdl:definitions/@targetNamespace"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>

Here is an example of how this XSL template is used:


Listing 6. Technique 2 example
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="wsdl-util.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<!-- all the namespaces are defined globally, so just pick
an arbitrary element -->
<xsl:template match="wsdl:definitions">
<xsl:text>namespace uri http://www.w3.org/2001/XMLSchema</xsl:text>
<xsl:text> mapped by prefix </xsl:text>
<xsl:call-template name="prefix-for-namespace">
<xsl:with-param name="namespace-uri"
select="'http://www.w3.org/2001/XMLSchema'"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
<xsl:text>targetNamepace uri mapped by prefix </xsl:text>
<xsl:call-template name="prefix-for-target-namespace"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Running the above example against the test WSDL document produces:
Listing 7. Technique 2 example output
namespace uri http://www.w3.org/2001/XMLSchema mapped by prefix xsd:
targetNamepace uri mapped by prefix test:

Technique 3: Extract the local name from a qname

The last of the qname techniques provides an analog to theXSL local-name() function.
As with the previous techniques XSL provides this function for nodes in the source
document, but the fact that WSDL uses an attribute value to store the qname
complicates matters.
Listing 8. Technique 3 XSL
<!-- emits the local name of the specified qname -->
<xsl:template name="local-name-of-qname">
<xsl:param name="qname"/>

<xsl:choose>
<xsl:when test="contains($qname,':')">
<xsl:value-of select="substring-after($qname,':')"/>
</xsl:when>

<xsl:otherwise>
<xsl:value-of select="$qname"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Here is an example of how this XSL template is used:


Listing 9. Technique 3 example
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="wsdl-util.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<!-- match an arbitrary element so we can invoke the template -->
<xsl:template match="wsdl:definitions">
<xsl:text>local name of foo:bar is </xsl:text>
<xsl:call-template name="local-name-of-qname">
<xsl:with-param name="qname" select="'foo:bar'"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
<xsl:text>local name of baz is </xsl:text>
<xsl:call-template name="local-name-of-qname">
<xsl:with-param name="qname" select="'baz'"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Running the above example against the test WSDL document produces:
Listing 10. Technique 3 example output
local name of foo:bar is bar
local name of baz is baz

Technique 4: Correlate portType operations with messages


When processing WSDL documents, it is often necessary to navigate
from <wsdl:portType>/<wsdl:operation> elements back to the<wsdl:message> elements
they refer to. Within the WSDL document, this is accomplished via the message attributes
on <wsdl:input>,<wsdl:output> and <wsdl:fault> messages. While it is tempting to use
simple XPath expressions to perform this navigation, this approach is not guaranteed to work in
all cases. In particular, this approach does not correctly handle multi-file WSDL documents or
inconsistent use of namespace prefixes within the WSDL document (both of which are perfectly
legal according to the specifications).

The approach shown below uses XSL named keys in conjunction with the templates
introduced in the previous techniques to build a robust solution to this problem.

Listing 11. Technique 4 XSL


<!-- index of wsdl:message by namespace uri, wsdl:message/@name -->
<xsl:key name="message"
match="wsdl:message"
use="concat(/wsdl:definitions/@targetNamespace,' ',@name)"/>

<!-- message key value that corresponds to a


wsdl:portType/wsdl:operation/wsdl:* -->
<xsl:template match="wsdl:portType/wsdl:operation/wsdl:input |
wsdl:portType/wsdl:operation/wsdl:output |
wsdl:portType/wsdl:operation/wsdl:fault"
mode="message-key">
<xsl:call-template name="namespace-uri-of-qname">
<xsl:with-param name="qname" select="@message"/>
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:call-template name="local-name-of-qname">
<xsl:with-param name="qname" select="@message"/>
</xsl:call-template>
</xsl:template>

Here is an example of how this XSL template is used:


Listing 12. Technique 4 example
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="wsdl-util.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<!-- dump out message part counts for all operations -->
<xsl:template match="wsdl:portType/wsdl:operation">
<xsl:value-of select="@name"/>
<xsl:text> has </xsl:text>

<!-- count input parts -->


<!-- get the correct value for the message name key -->
<xsl:variable name="input-message-key">
<xsl:apply-templates select="wsdl:input" mode="message-key"/>
</xsl:variable>
<xsl:value-of select="count(key('message',$input-message-key)/
wsdl:part)"/>
<xsl:text> input parts, </xsl:text>

<!-- count output parts -->


<!-- get the correct value for the message name key -->
<xsl:variable name="output-message-key">
<xsl:apply-templates select="wsdl:output" mode="message-key"/>
</xsl:variable>
<xsl:value-of select="count(key('message',$output-message-key)/
wsdl:part)"/>
<xsl:text> output parts, </xsl:text>

<!-- count fault parts -->


<!-- get the correct value for the message name key -->
<xsl:variable name="fault-message-key">
<xsl:apply-templates select="wsdl:fault" mode="message-key"/>
</xsl:variable>
<xsl:value-of select="count(key('message',$fault-message-key)/
wsdl:part)"/>
<xsl:text> fault parts</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Running the above example against the test WSDL document produces:

Listing 13. Technique 4 example output


porttype-op1
porttype-op2
porttype-op3
porttype-op4

has
has
has
has

1
1
1
0

input
input
input
input

parts,
parts,
parts,
parts,

0
1
1
1

output
output
output
output

parts,
parts,
parts,
parts,

0
0
0
0

fault
fault
fault
fault

parts
parts
parts
parts

Technique 5: Correlate binding operations with portType operations

A similar problem occurs when navigating from


a <wsdl:binding>/<wsdl:operation> element back to the
corresponding<wsdl:portType>/<wsdl:operation> element. As with the previous
technique, the XSL approach shown below uses named keys to solve the problem.
Listing 14. Technique 5 XSL
<!-- index of wsdl:portType by namespace uri, wsdl:portType/@name -->
<xsl:key name="porttype"
match="wsdl:portType"
use="concat(/wsdl:definitions/@targetNamespace,' ',@name)"/>

<!-- porttype key that corresponds to a wsdl:binding -->


<xsl:template match="wsdl:binding"
mode="porttype-key">
<xsl:call-template name="namespace-uri-of-qname">
<xsl:with-param name="qname" select="@type"/>
</xsl:call-template>

<xsl:text> </xsl:text>
<xsl:call-template name="local-name-of-qname">
<xsl:with-param name="qname" select="@type"/>
</xsl:call-template>
</xsl:template>

Here is an example of how this XSL template is used:


Listing 15. Technique 5 example
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="wsdl-util.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<!-- delve into each binding -->
<xsl:template match="wsdl:binding">
<xsl:text>binding: </xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="wsdl:operation"/>
</xsl:template>

<!-- dump out the messages associated with each binding operation -->
<xsl:template match="wsdl:binding/wsdl:operation">
<xsl:text> operation: </xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>
</xsl:text>
<xsl:variable name="porttype-key">
<xsl:apply-templates select=".." mode="porttype-key"/>
</xsl:variable>
<xsl:variable name="opname">
<xsl:value-of select="@name"/>
</xsl:variable>

<xsl:for-each select="wsdl:input">
<xsl:text> input: </xsl:text>
<xsl:value-of select="@name"/>
<xsl:text> -> message: </xsl:text>
<xsl:for-each select="key('porttype',$porttype-key)/
wsdl:operation[@name=$opname]/
wsdl:input[@name=current()/@name]">
<xsl:value-of select="@message"/>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:if test="not(wsdl:input)">
<xsl:text> (no inputs)
</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Running the above example against the test WSDL document produces:
Listing 16. Technique 5 example output
binding: binding
operation: porttype-op1
input: porttype-op1-input -> message: test:inputmsg1
operation: porttype-op2
input: porttype-op2-input -> message: test:inputmsg2
operation: porttype-op3
input: porttype-op3-input -> message: test:inputmsg3
operation: porttype-op4
(no inputs)

Technique 6: Correlate service ports with bindings

The final qname mapping is from the wsdl:service/wsdl:port back to the wsdl:binding.
Again, named keys provide the solution.
Listing 17. Technique 6 XSL
<!-- index of wsdl:binding by namespace uri and wsdl:binding/@name -->
<xsl:key name="binding" match="wsdl:binding"
use="concat(/wsdl:definitions/@targetNamespace,' ',@name)"/>

<!-- binding key value for wsdl:port -->


<xsl:template match="wsdl:port" mode="binding-key">
<xsl:call-template name="namespace-uri-of-qname">
<xsl:with-param name="qname" select="@binding"/>
</xsl:call-template>

<xsl:text> </xsl:text>
<xsl:call-template name="local-name-of-qname">
<xsl:with-param name="qname" select="@binding"/>
</xsl:call-template>
</xsl:template>

This example puts together many of the techniques described here to generate a
summary of the operations provided by each wsdl:servicein a WSDL document.
Listing 18. Technique 6 example
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">
<xsl:import href="wsdl-util.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>

<!-- just process the services -->


<xsl:template match="wsdl:definitions">
<xsl:apply-templates select="wsdl:service"/>
</xsl:template>

<!-- dump out some information for each service -->


<xsl:template match="wsdl:service">
<xsl:text>service: </xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="wsdl:port"/>
</xsl:template>

<!-- follow the linkages from the wsdl:port through the


wsdl:binding, back to the wsdl:portType and its
wsdl:operations -->
<xsl:template match="wsdl:port">
<!-- key for looking up the wsdl:binding -->
<xsl:variable name="binding-key">
<xsl:apply-templates select="." mode="binding-key"/>
</xsl:variable>
<xsl:for-each select="key('binding',$binding-key)">
<!-- key for looking up the wsdl:portType -->
<xsl:variable name="porttype-key">
<xsl:apply-templates select="." mode="porttype-key"/>
</xsl:variable>
<!-- process each operation in the wsdl:portType -->
<xsl:for-each select="key('porttype',$porttype-key)/wsdl:operation">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:for-each>
</xsl:template>

<!-- generate a Java-like method signature for each operation -->


<xsl:template match="wsdl:portType/wsdl:operation">
<xsl:text> </xsl:text>
<!-- return type -->
<xsl:for-each select="wsdl:output">
<!-- get the correct value for the message name key -->
<xsl:variable name="output-message-key">
<xsl:apply-templates select="." mode="message-key"/>
</xsl:variable>
<!-- output the type of the first part of the message -->
<xsl:for-each
select="key('message',$output-message-key)/wsdl:part[1]">
<xsl:value-of select="@type"/>
</xsl:for-each>
</xsl:for-each>
<xsl:if test="not(wsdl:output)">
<xsl:text>void</xsl:text>
</xsl:if>

<!-- method name -->


<xsl:text> </xsl:text>
<xsl:value-of select="@name"/>

<!-- arguments -->


<xsl:text>(</xsl:text>
<xsl:for-each select="wsdl:input">
<!-- get the correct value for the message name key -->
<xsl:variable name="input-message-key">
<xsl:apply-templates select="." mode="message-key"/>
</xsl:variable>
<!-- output the type of the type and name of each part of
the message -->
<xsl:for-each select="key('message',$input-message-key)/wsdl:part">
<xsl:value-of select="@type"/>
<xsl:text> </xsl:text>
<xsl:value-of select="@name"/>
<xsl:if test="not(last())">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
<xsl:text>)</xsl:text>

<!-- exceptions -->


<xsl:if test="wsdl:fault">

<xsl:text> throws </xsl:text>


<xsl:for-each select="wsdl:fault">
<!-- get the correct value for the message name key -->
<xsl:variable name="fault-message-key">
<xsl:apply-templates select="." mode="message-key"/>
</xsl:variable>
<xsl:for-each select="key('message',$fault-message-key)">
<xsl:value-of select="@name"/>
</xsl:for-each>
<xsl:if test="not(last())">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:if>
<xsl:text>;
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Running the above example against the test WSDL document produces:
Listing 19. Technique 6 example output
service: service
void porttype-op1(xsd:string inputmsg1-part);
xsd:base64Binary porttype-op2(xsd:decimal inputmsg2-part) throws faultmsg;
xsd:base64Binary porttype-op3(xsd:integer inputmsg3-part) throws faultmsg;
xsd:base64Binary porttype-op4();

Technique 7: Importing additional definitions

This is more of a best practice than a technique per se, but it has proven extremely
useful. When transforming WSDL documents into new WSDL documents, it is often
tempting to insert additional data types and messages directly into the new WSDL
document. If this new information is static (that is, does not depend on the input WSDL)
consider using the WSDL import mechanism instead. Importing these definitions gives
you the ability to change them later without re-transforming all the WSDL documents.
Due to the nature of this technique, it is not included in the downloadable wsdl-util.xsl.
Listing 20. Technique 7 XSL
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:preserve-space elements="*"/>

<!-- add a wsdl:import for mystuff.wsdl -->


<xsl:template match="wsdl:definitions">
<xsl:copy>
<xsl:copy-of select="@*"/>
<wsdl:import namespace="http://tempuri.org/mystuff"
location="mystuff.wsdl"/>
<xsl:copy-of select="node()| comment()|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Here are the relevant portions of test.wsdl after being updated by the XSL shown above:
Listing 21. Technique 7 example output
<?xml version="1.0"?>
<wsdl:definitions name="test"
targetNamespace="http://tempuri.org/test/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:test="http://tempuri.org/test/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:import namespace="http://tempuri.org/mystuff" location="mystuff.wsdl"/>
<wsdl:message name="inputmsg1">
<wsdl:part name="inputmsg1-part" type="xsd:string"/>
</wsdl:message>
...

Technique 8: Append a suffix to an attribute value

When transforming WSDL documents, it is often necessary to derive new and/or


additional definitions from existing definitions. To help preserve the origin of these new
definitions, it is often useful to assign them names similar to the source elements from
which they are derived. Due to the nature of this technique, it is not included in the
downloadable wsdl-util.xsl.
The following XSL illustrates one way to accomplish this by appending a suffix to the
existing name.
Listing 22. Technique 8 XSL
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:preserve-space elements="*"/>

<!-- Except as overridden by more specific templates below, do a


deep copy of everything. -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>

<!-- append a suffix to wsdl:portType/@names -->


<xsl:template match="wsdl:portType/@name">
<xsl:attribute name="{name(.)}">
<xsl:value-of select="."/>
<xsl:text>-mysuffix</xsl:text>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

Here are the relevant portions of test.wsdl after being updated by the XSL shown above:
Listing 23. Technique 8 example output
...
<wsdl:message name="faultmsg"/>

<wsdl:portType name="porttype-mysuffix">
<!-- one-way operation -->
<wsdl:operation name="porttype-op1">
<wsdl:input message="test:inputmsg1" name="porttype-op1-input"/>
</wsdl:operation>
...

Back to top
Themes

In conclusion, here are some higher-level themes from the techniques shown above for
processing WSDL documents with XSL. Hopefully they will save you some time and
mental anguish the next time you need to hack a WSDL document with XSL:

1.

2.

3.

4.

5.

Always be prepared for null namespaces on qualified names. While


this almost always adds complexity to the XSL transforms,
wsdl:definitions/@targetNamespace is optional.
Change namespace prefixes to URIs as soon as possible.
Namespace prefixes are basically meaningless to anyone except the
author of the original document and relying on a constant one-to-one
mapping of namespace prefixes to URIs is unwise at best.
When trying to correlate constructs in a WSDL document, keys are
almost always a better choice than XPath expressions. This is largely a
result of the previous two items, but in almost every case using keys
instead of XPath expressions results in simpler and shorter XSL
transforms.
Pay very close attention to current() when evaluating namespaces.
The XSL constructs for mapping from namespace prefixes to URIs work
best for current(). Trying to evaluate namespace prefixes for a node other
than current() is error-prone.
Import rather than augment. When adding new types and messages
to a WSDL document, it is tempting to simply include the definitions in the
output WSDL document. Unless these changes are cast in concrete (for
example, part of a published standard), it is almost always a better idea to
place the new definitions in a separate WSDL document and add an import
element to the generated document. Doing so makes future changes
significantly less painful.

6.

Be prepared for multi-part WSDL documents. While this might not be


a common usage pattern with current tooling, the WSDL specification
specifically describes how to split a WSDL document across multiple files.
Don't assume that simple XPath expressions will correctly navigate among
sections of the WSDL document that reside in different input files. Multi-part
WSDL documents can also have more subtle effects. For example, it is not
always possible to determine if a message is used as an input or output
since the definitions of the message and the operations that use it can be in
separate files.
XSL transforms allow you to make complex changes to WSDL documents in an
automated fashion. The approaches and XSL snippets contained in this article can give
you a jump start processing WSDL documents and help you avoid the most common
pitfalls

There are the following distinct variable types, each expressed in


the var://URL format and GatewayScript format, as appropriate.
var://local/variable

A local context variable to address a variable that is


called variable in the default (current) context. The following
example transforms the document in the tmp1 context with a
style sheet. The style sheet is referenced by the stylesheet1 variable (also in the tmp1 context) and stores the
transformed document in the tmp2 context:
xform tmp1 var://local/stylesheet-1 tmp2

The local context does not persist beyond the scope of the transaction. A
transaction can include both a request component and a response component.
The local context cannot be accessed by any object outside the scope of the
transaction. In other words, a service cannot read and use the variable.
A local context variable can be user-defined or based on an extension variable.
var://context/context/variable
Addresses a variable that is called variable in a context
called context. The following example transforms the document
in the tmp1context with a style sheet. The style sheet is
referenced by the stylesheet-1 variable (in
the apple context) and stores the transformed document in
the tmp2 context:
xform tmp1 var://context/apple/stylesheet-1 tmp2

A named context does not persist beyond the scope of the transaction. A
transaction can include both a request component and a response component.
The local context cannot be accessed by any object outside of the scope of the
transaction. In other words, the service cannot read and use the variable.
Note: Creating variables in a named context is the preferred
approach. This form decouples the variable from the input and
output contexts and allows the variable to be accessed from
any step in a scope.
A named context variable can be user-defined or can be based on an extension
variable.

var://service/variable
Addresses a variable that is made available to a DataPower
service that is attached to a session.
Many service variables are supported in XSLT and GatewayScript . For
example, the variable skip-backside has the XSLT
formvar://service/mpgw/skip-backside and
the GatewayScript form mpgw.skipBackside. The term "slash notation"
refers to the XSLT name form and the term "dot notation" refers to
the GatewayScript name form. GatewayScript variables that are not supported
do not appear in the syntax section of the description of the variable.
Calls to GatewayScript variables support both forms of names.
A program that uses the dot notation form establishes access
to the service variables in two steps: requiring the module that
holds the service variables (service-metadata) and setting a
variable to access the variables. For example:
var serviceVars = require ( 'service-metadata' ); // import the
service variables functions
serviceVars.mpgw.skipBackside = true;
// write to the
skipbackside service variable

In the information for each variable that is supported by GatewayScript, the


specifications assume a require statement (var
serviceVars=require ('service-metadata')) already
established access. You must establish access to the service metadata by using
a require statement in your code (naming serviceVars or other variable)
to manipulate the use service variable. The skipBacksidevariable call
uses serviceVars.
A GatewayScript program establishes access to an XSLT service
variable by using the slash notation name form
in getVar and setVarfunction calls. For example:
var genPattern = serviceVars.GetVar("var://service/wsa/genpattern");

Both these name forms are listed in the function information where both are
supported. When the GatewayScript name form is not in the list,
the GatewayScript form is not supported. GatewayScript checks the data type
when a new value is assigned to a service variable. The gettermethod returns
a value with the correct data type.
var://system/variable
Addresses a global variable that is available in all contexts.
System variables persist beyond the scope of request-response
processing and can be read by other objects on the appliance.
If the content of a variable needs to be read or set outside the
scope of request-response processing, use a system variable.
Service variables
Service variables enable the setting and retrieval of pieces of
state that usually reflect the state of the current transaction.
Parent topic: DataPower Gateway

Integrating WebSphere DataPower XML


Security Gateway XS40 with WebSphere
Message Broker
Introduction

The following diagram illustrates the scenario described in this article:


Figure 1. The full scenario

The requesting application communicates to an IBM WebSphere DataPower XML


Security Gateway XS40 (hereafter called the DataPower appliance) using SOAP over
HTTP, with the message body encrypted to the WS-Security standard. The DataPower
appliance decrypts the body of the message it receives, and this content is then passed
to WebSphere Message Broker (hereafter called Message Broker) over a connection
secured by HTTPS. Message Broker receives the SOAP message and transforms it into
a COBOL structure for the final WebSphere MQ application. The responses then flow
back in a similar fashion. The initial configuration uses simple HTTP between the
DataPower appliance and Message Broker. The modifications to use HTTPS are
performed as a second stage of the configuration.
The components described here can be placed within an SOA design pattern in which
the DataPower appliance provisions WS-Security within the demilitarized zone that
protects an enterprise.
Figure 2. The scenario aligned within a demilitarized zone

Configuration

The following sections concentrate on the configuration of DataPower and on the


relevant externals that Message Broker presents to DataPower.
Configuring the DataPower appliance
Figure 3. Focusing on the DataPower configuration

WS-Security

WS-Security describes enhancements to SOAP messaging to provide quality of


protection through message integrity, message confidentiality, and single message
authentication. These mechanisms can be used to accommodate a wide variety of
security models and encryption technologies.
The DataPower appliance is configured with a simple XML firewall with a static backend
that connects to the Message Brokers HTTP listener. After the main page of the XML
firewall is configured,an addition must be made to the Headers page before a
processing policy is associated with the XML firewall. This processing policy provides
the encryption and decryption of the SOAP body using the WS-Security standard.
Figure 4 below shows the main configuration of the XML firewall with the following items
configured:

Name and summary


Server address and port
Device port
Request and response type set to SOAP

Figure 4. Configuration of the XML firewall

Once the header has been configured as described in the next section, the firewall
policy is added.
Headers

In the firmware version, the DataPower appliance generated two connection header
tags. By adding suppression for this tag, only one connection header is sent by the

DataPower appliance. Figure 5 shows the Headers page with the connection header tag
suppressed in the "back" direction:
Figure 5. Configuring the headers

Processing policy

The processing policy includes just two rules: the Request rule decrypts the incoming
message body, and the Response rule encrypts the message body of the reply.
Following the traditions of security literature, the keys used in this article are named
AliceKey and BobKey. The requesting application sends messages encrypted with the
AliceKey, and the DataPower appliance encrypts the reply with the BobKey.
Request (decryption) rule

The Request rule includes a match rule property and a decrypt action as in the following
figure. The matching rule property matches on all URLs.
Figure 6. The decryption rule

The decrypt action is configured to decrypt the incoming message as determined from
the WS-Security headers and information contained within the SOAP message. As the
requesting application has encrypted the message with the AliceKey, it is used for
decryption within the DataPower appliance.
Figure 7. The decryption action

Response (encryption) rule

The response rule performs the opposite, encrypting the messages flowing in this
direction:
Figure 8. The encryption rule

The encryption action is configured to encrypt the SOAP message body using WSSecurity, and it uses the predefined key BobKey:
Figure 9. The encryption action

The private half of the BobKey is owned by the requesting application, so that it can
decrypt the message sent by the DataPower appliance.
Configuring Message Broker

On receiving a decrypted SOAP/HTTP message from the DataPower appliance, a


message flow running in a Message Broker instance converts the incoming message
into a fixed length message in a COBOL format, which is then processed by a separate
WebSphere MQ application. The message is updated and passed to a second message
flow, which converts the message into a SOAP/HTTP message containing the reply:
Figure 10. Focusing on the Message Broker function

The processing being performed is that of the Web services sample WSHOST, which is
supplied with WebSphere Message Broker V6. For more information about this sample,
go to the Message Broker toolkit and select Help => Samples Gallery on the Toolkit
menu:
Figure 11. The two WebSphere Message Broker message flows

The first message flow processes the incoming SOAP/HTTP message after t has been
decrypted by the DataPower appliance. After converting the message format and
protocol, it writes an output message to a WebSphere MQ queue, which is read and
processed by the WebSphere MQ application.
The second message flow receives the WebSphere MQ reply message from the
WebSphere MQ application and converts the format and protocol of the input message
into a SOAP/HTTP reply message. The reply message generated by the second
message flow is passed to the DataPower appliance, so that it can be encrypted prior to
being returned to the requesting application.
HTTP Input node properties

The input node properties show the URL on which this flow is configured to receive
requests. The HTTPS box remains unchecked -- a later section enables it to complete
the security.
WebSphere MQ application

The WebSphere MQ application provided with the WSHOST sample performs an MQ


Get on its input queue waiting for messages from the broker. When one is received, it
modifies the message and writes a reply to a queue that will be read by a message flow.
Messages

The following sections show the messages that flow within the system to and from the
DataPower appliance. Figure 12 below shows the placement of these messages within
the overall flow. Messages between Message Broker and the backend WebSphere MQ
application are not shown.
Figure 12. Highlighting the messages described in this section

Message sent from the requesting application to the DataPower appliance

Shown below is the HTTP body of the message sent to the DataPower appliance by the
requesting application. The real content of the message in its encrypted form is
highlighted (in bold) and has been truncated for readability. For the full contents of this
and subsequent messages, see Downloads below.
Message 1. The message sent from the application to the DataPower appliance

Click to see code listing


Message sent from the DataPower appliance to WMB

Here is the HTTP body of the message sent by the DataPower appliance to Message
Broker. The body (in bold) has been decrypted to reveal the SOAP request:
Message 2. Message sent from the DataPower appliance to Message Broker
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:c="http://www.brokersamplewshost.ibm.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wsswssecurity-secext-1.0.xsd">
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<c:IA81CONFIN>
<MessageId>IA81CONF</MessageId>
<OrderNumber>ON4002</OrderNumber>
<ItemReference>IY4003</ItemReference>
<ItemQuantity>4</ItemQuantity>
<CustomerNumber>CY4004</CustomerNumber>
</c:IA81CONFIN>
</soapenv:Body>
</soapenv:Envelope>

Message sent from Message Broker to the DataPower appliance

Here is the HTTP body of the message returned by Message Broker to the DataPower
appliance. For this return trip, two additional fields, DeliveryRef and Confirm (in bold),
are included at the end of the message:
Message 3. Message sent from Message Broker to the DataPower appliance
<?xml version="1.0"?>
<tns:Envelope xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:NS1="http://www.brokersamplewshost.ibm.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tns:Body>
<NS1:IA81CONFOUT>
<MessageId>IA81CONF</MessageId>
<OrderNumber>ON4002</OrderNumber>
<ItemReference>IY4003</ItemReference>
<ItemQuantity>4</ItemQuantity>
<CustomerNumber>CY4004</CustomerNumber>
<DeliveryRef>JOHNCORP</DeliveryRef>
<Confirm>Y</Confirm>
</NS1:IA81CONFOUT>
</tns:Body>
</tns:Envelope>

Message sent by the DataPower appliance to the requesting application

The HTTP body of the message returned by the DataPower appliance to the requesting
application follows a similar pattern to the first message sent to the DataPower
appliance. The difference is that the contents of this message is encrypted using the
return key. Decrypting this message would reveal the message sent by Message
Broker.
Message 4. Message sent from the DataPower appliance to the application

Click to see code listing

Securing the connection between the DataPower appliance and Message Broker

Adding SSL support between the DataPower appliance and Message Broker completes
the security of the connections between them:
Figure 13. Securing the link between the DataPower appliance and Message Broker

SSL enabling HTTP for Message Broker

To enable SSL for Message Broker, it must first be configured and assigned a
certificate. For this article, a self-signed certificate within a key store is used by the
Message Broker HTTP listener process. You could also use a certificate signed by a
certificate authority. The broker is then configured to use this key store.
The Java ikeyman application is used to create the key store. To create a self-signed
certificate stored within this key store, select New Self-Signed Certificate. For this
article the following configuration is used:
Figure 14. The self-signed certificate in ikeyman

To provide the public half of this certificate to the the DataPower appliance,
select Extract certificate. The following commands enable SSL, configure the key
store, provide the password for this key store, and set the HTTPS port:
Listing 4. The commands to enable HTTPS in Message Broker
>mqsichangeproperties WBRK6_DEFAULT_BROKER -b httplistener -o HTTPListener
-n enableSSLConnector -v true
>mqsichangeproperties WBRK6_DEFAULT_BROKER -b httplistener -o HTTPSConnector
-n keystoreFile -v "c:\Program Files\IBM\WMBv6.0\MyKeystore.jks"
>mqsichangeproperties WBRK6_DEFAULT_BROKER -b httplistener -o HTTPSConnector
-n keystorePass -v ********
>mqsichangeproperties WBRK6_DEFAULT_BROKER -b httplistener -o HTTPSConnector
-n port -v 7083

The broker must be restarted for these changes to take effect within the HTTP listener
process. You can then use the following two commands to verify the previous settings:
Listing 5. The commands report the HTTPS settings in Message Broker
>mqsireportproperties WBRK6_DEFAULT_BROKER -b httplistener -o HTTPListener -a
HTTPListener=''
uuid='HTTPListener'
enableSSLConnector='true'
traceLevel='none'
traceSize='4194304'
BIP8071I: Successful command completion.
>mqsireportproperties WBRK6_DEFAULT_BROKER -b httplistener -o HTTPSConnector -a

HTTPSConnector=''
uuid='HTTPSConnector'
keystoreFile='c:\Program Files\IBM\WMBv6.0\MyKeystore.jks'
keystorePass='********'
port='7083'
BIP8071I: Successful command completion.

Enable the message flow for SSL

With the broker restarted, you can configure the HTTP input node for HTTPS and
redeploy the message flow to the broker. Figure 15 shows the properties of the HTTP
Input node within the message flow, with Use HTTPS checked:
Figure 15. Properties of the HTTP node with the Use HTTPS item checked

You can use the netstat -a (or equivalent) command after Message Broker is restarted
and an HTTPS flow is deployed, to confirm that the HTTP listener is listening on the
configured port.
Configuring client-side SSL in the DataPower appliance

You must upload a certificate of the key generated for Message Broker into the
DataPower appliance, which is added as a trusted server within an SSL profile that is
then used for the SSL client crypto profile. To create and associate an SSL client crypto
profile, select Create on the main XML Firewall configuration page. To configure the
profile upload, then add the certificate exported from ikeyman to the Trusted Servers
section of the crypto profile. Select the option to authenticate and validate the certificate.
With this check the DataPower appliance can be trusted to connect only to trusted
servers.
Figure 16. The Trusted Server configuration

You must also reconfigure the XML firewall to point to the SSL port of the HTTP listener
(in this case the default value of 7083).
SSL encryption

To observe this change, an HTTP tunnel is placed between the DataPower appliance
and Message Broker and used to observe messages that pass in both directions
between the two components. Whilst some of the initial exchange that forms the SSL
protocol passes as clear text, after the private keys are established for the session, all
data is encrypted.
Message Broker Explorer

Message Broker Explorer (IS02 support pack) now includes a wizard that can configure
an XML firewall within the DataPower appliance for connection with HTTP flows
provisioned by Message Broker. For more information, see the first article below under
"Resources."

Conclusion

This article has demonstrated how you can use a WebSphere DataPower SOA
appliance to extend the function provided by WebSphere Message Broker and provide
WS-Security of SOAP messages to HTTP message flows. The article also showed how
to provide security between WebSphere Message Broker and the DataPow

You might also like