Skip to content

Instantly share code, notes, and snippets.

@pglezen
Last active May 20, 2021 09:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pglezen/0f6bbe5aaec8fc526156 to your computer and use it in GitHub Desktop.
Save pglezen/0f6bbe5aaec8fc526156 to your computer and use it in GitHub Desktop.
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>
@dpduser
Copy link

dpduser commented Dec 14, 2020

Can you please provide a sample on how to route single request to multiple backends based on condition using XSLT ?

@pglezen
Copy link
Author

pglezen commented Dec 14, 2020

Sorry @dpduser; I haven't worked with DataPower in a deep way for several years. Perhaps IBM support can provide some tips.

@ManojDusa
Copy link

ManojDusa commented May 20, 2021

Can you please provide a sample on how to route single request to multiple backends based on condition using XSLT ?

you can set service/routing-url in your xslt condition to send payload to different target servers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment