Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
DataPower Dynamic Routing

The code sample below demonstrates how to implement a dynamic routing scheme in DataPower. It uses the inbound URI as the routing criterion. The routing logic is contained in the XSLT named computeRoute.xsl. The XSLT reads the route configuration from an XML file named routes.xsl.

All matches are logged to a log category named route-<domain> where represents the name of the application domain. Defining a log category with this name will enable you to fine tune the destination and detail in which the route logs are generated. This is optional; in the absence of such a log category, these statements are channeled into the xsltmsg log category.

An attempt is made to find the longest match. In the case of the sample routes.xml provided,

/app1/endpoint2/abc

will match the first two routes. But because the second route matches more of the URI than the first route, the second route should be chosen.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
xmlns:dpconfig="http://www.datapower.com/param/config"
exclude-result-prefixes="dp dpconfig"
extension-element-prefixes="dp"
version="1.0">
<!--
This stylesheet assigns dynamic routing information based
on the inbound URI and and a routing file named route.xml.
The routing file entry with the *longest* match to the
inbound URI will be chosen. (Since wild-cards are allowed,
there could be multiple candidate matches). If there are
no matches, the request is rejected.
-->
<dp:summary xmlns="">
<operation>xform</operation>
<description>
Determine a dynamic route based on the inbound URI and
a route file, specified as an input parameter.
</description>
</dp:summary>
<xsl:param name="dpconfig:RouteFile" select="'routes.xml'"/>
<dp:param name="dpconfig:RouteFile" type="dmString" xmlns="">
<display>Route File</display>
<description>
Provide the relative path to a route file that
determines the location of downstream services.
The default is routes.xml.
</description>
<default>routes.xml</default>
</dp:param>
<xsl:variable name="domain" select="dp:variable('var://service/domain-name')"/>
<xsl:variable name="category" select="concat('route-',$domain)"/>
<xsl:template match="/">
<xsl:variable name="routes" select="document($dpconfig:RouteFile)/routes"/>
<xsl:variable name="uri" select="dp:variable('var://service/URI')"/>
<!--
Collect all service elements with a uri attribute that matches the
beginning of the inbound URI.
-->
<xsl:variable name="service-matches" select="$routes/service[starts-with($uri, @uri)]"/>
<!-- Reject if there are no matches. Log if there are multiple ones. -->
<xsl:choose>
<xsl:when test="1 &gt; count($service-matches)">
<xsl:message dp:priority="warn" dp:type="{$category}">
Route match failure.
Inbound URI = <xsl:value-of select="$uri"/>
</xsl:message>
<dp:reject>
No URI match for <xsl:value-of select="$uri"/>
using route file <xsl:value-of select="$dpconfig:RouteFile"/>
</dp:reject>
</xsl:when>
<xsl:when test="1 &lt; count($service-matches)">
<xsl:message dp:priority="info" dp:type="{$category}">
Found <xsl:value-of select="count($service-matches)"/> URI matches.
Matching most specific one.
</xsl:message>
</xsl:when>
</xsl:choose>
<!--
Of all the service matches in $service-matches, extract the biggest
URI from among them. Then use that biggest URI to select the service.
-->
<xsl:variable name="biggest-uri">
<xsl:call-template name="BiggestMatch">
<xsl:with-param name="services" select="$service-matches"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="service" select="$service-matches[@uri=$biggest-uri]"/>
<!-- Assign DataPower dynamic routing info. -->
<dp:set-variable name="'var://service/routing-url-sslprofile'"
value="$service/sslPP"/>
<dp:set-variable name="'var://service/routing-url'"
value="concat('https://', $service/host, ':', $service/port, $uri)"/>
<!-- Log some results for debugging purposes. -->
<xsl:message dp:priority="notice" dp:type="{$category}">
Inbound URI = <xsl:value-of select="$uri"/>
</xsl:message>
<xsl:message dp:priority="info" dp:type="{$category}">
Service Target = <xsl:value-of select="$service/host"/>:<xsl:value-of select="$service/port"/>
</xsl:message>
<xsl:message dp:priority="debug" dp:type="{$category}">
Route File = <xsl:value-of select="$dpconfig:RouteFile"/>
</xsl:message>
<xsl:message dp:priority="debug" dp:type="{$category}">
SSL Proxy Profile = <xsl:value-of select="$service/sslPP"/>
</xsl:message>
<xsl:message dp:priority="debug" dp:type="{$category}">
URI Match Pattern = <xsl:value-of select="$biggest-uri"/>
</xsl:message>
<xsl:message dp:priority="debug" dp:type="{$category}">
Service Name = <xsl:value-of select="$service/name"/>
</xsl:message>
</xsl:template>
<!--
Recursively iterate through matches to find most specific URL
-->
<xsl:template name="BiggestMatch">
<xsl:param name="services"/>
<xsl:param name="index" select="1"/>
<xsl:param name="bigName" select="''"/>
<xsl:variable name="currentName" select="$services[$index]/@uri"/>
<xsl:variable name="biggerName">
<xsl:choose>
<xsl:when test="string-length($bigName) &gt; string-length($currentName)">
<xsl:value-of select="$bigName"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$currentName"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$index &lt; count($services)">
<xsl:call-template name="BiggestMatch">
<xsl:with-param name="services" select="$services"/>
<xsl:with-param name="index" select="$index + 1"/>
<xsl:with-param name="bigName" select="$biggerName"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$biggerName"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="utf-8"?>
<!--
DP route configuration file.
For each service URL, specify the host and port.
The sslPP is optional (SSL Proxy Profile).
The name is used only for logging.
-->
<routes>
<service uri="/app1">
<host>app1.acme.com</host>
<port>443</port>
<name>Endpoint 1 Application</name>
<sslPP>AcmePP</sslPP>
</service>
<service uri="/app1/endpoint2">
<host>app1.acm.com</host>
<port>8080</port>
<name>Endpoint 2 Application</name>
</service>
<!-- App 2 routes -->
<service uri="/app2">
<host>app2.acme.com</host>
<port>9080</port>
<name>Application 2</name>
</service>
</routes>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment