Friday 1 February 2013

Sending and Saving Emails in SoaSuite

My current project is about the revisiting of some workflows built in SoaSuite10g (Bpel, Esb).
In these workflows emails are sent at several point. Some of these mails are about errors to the administrator. But others are for customers/applicatants or end-users.

In one of these cases  I have to add a new email that is to be saved in to the Document Management System (DMS) after being send.

Now in a typical use of the email-notification service, the text of the message is put in the Notification activity in the bpel process. That means that the text is fixed/hard-coded in the Bpel code. Changing the text means changing the bpel process, and have it through the whole test/acceptance/production cycle.

In this particular case the message is to be determined up-front, since I need to save the same message to file. One of my "rescues" is the lookupXML function I talked about in my previous post. The other one is the lovely XSL-language.

First I created an XML-file file as follows:

<?xml version="1.0" encoding="windows-1252" ?>
<!-- $Id: email_texts.xml 99 2013-02-01 08:27:07Z ca_mavandenakker $ -->
<!-- This file can be located in a HTTP accessible directory in the midtier installation and it's referred 
     through an lookup-xml XPath expression in the service invocation.  -->
<!-- The current location is http://${deploy.soasuite.URL}/ObjectLibrary/EmailService/xml/email_texts.xml -->
<email_texts>
   <email_text>
      <key>EmailSubject1</key>
      <value>Subject Email 1</value>
   </email_text>
   <email_text>
      <key>Body Email 1</key>
      <value>Dear Mailer, &lt;br/>
Thank you for sending these documents:&lt;br/>
&lt;table>

 &lt;tr>
  &lt;td colspan =&quot;2&quot;>
   ${ReceivedDocuments}
  &lt;td>
 &lt;/tr>
  &lt;tr>
  &lt;td>Indiener:&lt;/td>&lt;td>${ApplicantName}&lt;td>
 &lt;/tr>
 &lt;tr>
  &lt;td>Telefoonnummer:&lt;/td>&lt;td>${ApplicantPhone}&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>E-mailadres:&lt;/td>&lt;td>${ApplicantEmail}&lt;/td>
 &lt;/tr>
  
 &lt;tr>
  &lt;td colspan=&quot;2&quot;>&lt;br>&lt;b>Your appointed reprecentative:&lt;/b>&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>Naam:&lt;/td>&lt;td>${ReprecentativeFullname}&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>Telefoon:&lt;/td>&lt;td>${ReprecentativePhone}&lt;/td>
 &lt;/tr>
 &lt;tr>
  &lt;td>E-mail:&lt;/td>&lt;td>${ReprecentativeUsername}@darwin-it.nl&lt;/td>
 &lt;/tr>
&lt;/table> 
&lt;br/>
Your application is taken in account.

Remark: this is an  automatically generated message. Replies on this message will not be answered.&lt;br/>
&lt;br/>
Best regards,&lt;br/>
&lt;br/>
Darwin-IT Professionals&lt;br/>
</value>
   </email_text>
</email_texts>

This file can be either on the file system of the SOAServer or on a particular folder in the docRoot of the HTTP server of the SoaServer. In 11g you would put it in the MDS.

As you can see in the example, I put several properties into the document. Properties in the form of ${property}, like properties in ANT.

These have to be replaced by the process giving a list of properties. I created a bpel process that does this. And use the result to send the email message. In the response the resulting subject and body are returned, so that they can be used to save to a html-document in a temporary folder on the filesystem.

The xsd of the bpel process is as follows:
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
            xmlns:svnId="$Id" xmlns:version="1.0"
            targetNamespace="http://www.darwin-it.nl/xsd/v1/EmailService"
            xmlns="http://www.darwin-it.nl/xsd/v1/EmailService"
            xmlns:ems="http://www.darwin-it.nl/xsd/v1/EmailService"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <xsd:element name="EmailServiceProcessRequest"
              type="ems:EmailServiceProcessRequestType"/>
 <xsd:element name="EmailServiceProcessResponse"
              type="ems:EmailServiceProcessResponseType"/>
 <xsd:complexType name="EmailServiceProcessRequestType">
  <xsd:sequence>
   <xsd:element name="FromAccountName" type="xsd:string" minOccurs="0"/>
   <xsd:element name="To" type="xsd:string"/>
   <xsd:element name="Cc" type="xsd:string" minOccurs="0"/>
   <xsd:element name="Bcc" type="xsd:string" minOccurs="0"/>
   <xsd:element name="ReplyToAddress" type="xsd:string" minOccurs="0"/>
   <xsd:element name="subjectKey" type="xsd:string"/>
   <xsd:element name="bodyKey" type="xsd:string"/>
   <xsd:element name="PropertyList" type="ems:PropertyList" minOccurs="0"/>
  </xsd:sequence>
 </xsd:complexType>
 <xsd:complexType name="EmailServiceProcessResponseType">
  <xsd:sequence>
   <xsd:element name="To" type="xsd:string"/>
   <xsd:element name="Cc" type="xsd:string" minOccurs="0"/>
   <xsd:element name="EmailBody" type="xsd:string"/>
   <xsd:element name="EmailSubject" type="xsd:string"/>
  </xsd:sequence>
 </xsd:complexType>
 <xsd:complexType name="PropertyType">
  <xsd:sequence>
   <xsd:element name="name" type="xsd:string"/>
   <xsd:element name="value" type="xsd:string"/>
  </xsd:sequence>
 </xsd:complexType>
 <xsd:element name="Property" type="ems:PropertyType"/>
 <xsd:complexType name="PropertyList">
  <xsd:sequence>
   <xsd:element name="Property" type="ems:PropertyType" maxOccurs="unbounded"/>
  </xsd:sequence>
 </xsd:complexType>
</xsd:schema>

The subjectKey and bodyKey elements correspond to the key values in the email.xml. With those values the XSL will lookup the particular template text. In the process the following xsl is used to do the transformation:
<?xml version="1.0" encoding="UTF-8" ?>
<?oracle-xsl-mapper
  <!-- SPECIFICATION OF MAP SOURCES AND TARGETS, DO NOT MODIFY. -->
  <mapSources>
    <source type="XSD">
      <schema location="EmailService.xsd"/>
      <rootElement name="EmailServiceProcessRequest" namespace="http://www.darwin-it.nl/xsd/v1/EmailService"/>
    </source>
  </mapSources>
  <mapTargets>
    <target type="XSD">
      <schema location="EmailService.xsd"/>
      <rootElement name="EmailServiceProcessResponse" namespace="http://www.darwin-it.nl/xsd/v1/EmailService"/>
    </target>
  </mapTargets>
  <!-- GENERATED BY ORACLE XSL MAPPER 10.1.3.5.0(build 090730.0200.1754) AT [WED JAN 30 10:49:55 CET 2013]. -->
?>
<xsl:stylesheet version="1.0" xmlns:svnId="$Id"
                xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
                xmlns:ehdr="http://www.oracle.com/XSL/Transform/java/oracle.tip.esb.server.headers.ESBHeaderFunctions"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:version="1.0"
                xmlns:hwf="http://xmlns.oracle.com/bpel/workflow/xpath"
                xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20"
                xmlns:xref="http://www.oracle.com/XSL/Transform/java/oracle.tip.xref.xpath.XRefXPathFunctions"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ora="http://schemas.oracle.com/xpath/extension"
                xmlns:ids="http://xmlns.oracle.com/bpel/services/IdentityService/xpath"
                xmlns:orcl="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc"
                xmlns:ns0="http://www.darwin-it.nl/xsd/v1/EmailService"
                exclude-result-prefixes="xsl svnId xsd version ns0 bpws ehdr hwf xp20 xref ora ids orcl">
  <xsl:variable name="configURI"
                select="'http://melia03.hhdelfland.nl/ObjectLibrary/EmailServices/xml/email_texts.xml'"/>
  <xsl:variable name="test"/>
  <xsl:template match="/">
    <ns0:EmailServiceProcessResponse>
      <ns0:To>
        <xsl:value-of select="/ns0:EmailServiceProcessRequest/ns0:To"/>
      </ns0:To>
      <ns0:Cc>
        <xsl:value-of select="/ns0:EmailServiceProcessRequest/ns0:Cc"/>
      </ns0:Cc>
      <ns0:EmailBody>
        <xsl:call-template name="getEmailText">
          <xsl:with-param name="key"
                          select="/ns0:EmailServiceProcessRequest/ns0:bodyKey"/>
          <xsl:with-param name="propList"
                          select="/ns0:EmailServiceProcessRequest/ns0:PropertyList"/>
        </xsl:call-template>
      </ns0:EmailBody>
      <ns0:EmailSubject>
        <xsl:call-template name="getEmailText">
          <xsl:with-param name="key"
                          select="/ns0:EmailServiceProcessRequest/ns0:subjectKey"/>
          <xsl:with-param name="propList"
                          select="/ns0:EmailServiceProcessRequest/ns0:PropertyList"/>
        </xsl:call-template>
      </ns0:EmailSubject>
    </ns0:EmailServiceProcessResponse>
  </xsl:template>
   
  <!--  User Defined Templates  -->
   
  <xsl:template name="getEmailText">
    <xsl:param name="key"/>
    <xsl:param name="propList"/>
    <xsl:variable name="emailTextTpl"
                  select='orcl:lookup-xml($configURI,"/email_texts/email_text","key","value",$key)'/>
    <xsl:variable name="emailText">
      <!-- <xsl:value-of select="$emailTextTpl"/> -->
      <xsl:call-template name="replaceProps">
        <xsl:with-param name="string" select="$emailTextTpl"/>
        <xsl:with-param name="propList" select="$propList"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$emailText"/>
  </xsl:template>
   
  <xsl:template name="replaceProps">
    <xsl:param name="string"/>
    <xsl:param name="propList"/>
    <xsl:variable name="parm">
      <xsl:call-template name="getFirstProp">
        <xsl:with-param name="string" select="$string"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="parmVal"
                  select="$propList/ns0:Property[ns0:name=$parm]/ns0:value"/>
    <xsl:variable name="newString">
      <xsl:choose>
        <xsl:when test="string-length($parm)>0">
          <!-- There is a parameter so recusively call replaceProps with a replaced string -->
          <xsl:call-template name="replaceProps">
            <xsl:with-param name="string">
              <!-- Replace the parameter with the value and use that as a parameter for the recursively called template -->
              <xsl:call-template name="replace-ci">
                <xsl:with-param name="input" select="$string"/>
                <xsl:with-param name="fromStr" select="concat('${',$parm,'}')"/>
                <xsl:with-param name="toStr" select="$parmVal"/>
              </xsl:call-template>
            </xsl:with-param>
            <xsl:with-param name="propList" select="$propList"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <!-- No parameters left so just return the string -->
          <xsl:value-of select="$string"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="$newString"/>
  </xsl:template>
   
  <xsl:template name="replace-ci">
    <xsl:param name="input"/>
    <xsl:param name="fromStr"/>
    <xsl:param name="toStr"/>
    <xsl:if test="string-length( $input ) > 0">
      <xsl:variable name="startPos">
        <xsl:call-template name="index-of-ci">
          <xsl:with-param name="string" select="$input"/>
          <xsl:with-param name="search" select="$fromStr"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="inputLwr" select="xp20:lower-case($input)"/>
      <xsl:variable name="fromStrLwr" select="xp20:lower-case($fromStr)"/>
      <xsl:choose>
        <xsl:when test="contains( $inputLwr, $fromStrLwr )">
          <xsl:variable name="stringBefore"
                        select="substring($input,1,$startPos - 1)"/>
          <xsl:variable name="stringAfter"
                        select="substring($input,$startPos + string-length($fromStr))"/>
          <xsl:value-of select="concat($stringBefore,$toStr)"/>
          <xsl:call-template name="replace-ci">
            <xsl:with-param name="input" select="$stringAfter"/>
            <xsl:with-param name="fromStr" select="$fromStr"/>
            <xsl:with-param name="toStr" select="$toStr"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$input"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>
   
  <xsl:template name="index-of-ci">
    <xsl:param name="string"/>
    <xsl:param name="search"/>
    <xsl:param name="startPos"/>
    <xsl:variable name="searchLwr" select="xp20:lower-case($search)"/>
    <xsl:variable name="work">
      <xsl:choose>
        <xsl:when test="string-length($startPos)>0">
          <xsl:value-of select="substring($string,$startPos)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$string"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="stringLwr" select="xp20:lower-case($work)"/>
    <xsl:variable name="result">
      <xsl:choose>
        <xsl:when test="contains($stringLwr,$searchLwr)">
          <xsl:variable name="stringBefore">
            <xsl:value-of select="substring-before($stringLwr,$searchLwr)"/>
          </xsl:variable>
          <xsl:choose>
            <xsl:when test="string-length($startPos)>0">
              <xsl:value-of select="$startPos +string-length($stringBefore)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="1 + string-length($stringBefore)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          -1
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:copy-of select="$result"/>
  </xsl:template>
   
  <xsl:template name="getFirstProp">
    <xsl:param name="string"/>
    <xsl:variable name="startPosParm">
      <xsl:call-template name="index-of-ci">
        <xsl:with-param name="string" select="$string"/>
        <xsl:with-param name="search" select="'${'"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="endPosParm">
      <xsl:call-template name="index-of-ci">
        <xsl:with-param name="string" select="$string"/>
        <xsl:with-param name="search" select="'}'"/>
        <xsl:with-param name="startPos" select="$startPosParm"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="parmNameLength"
                  select="number($endPosParm)-(number($startPosParm)+2)"/>
    <xsl:variable name="parm">
      <xsl:choose>
        <xsl:when test="number($startPosParm)>0 and number($parmNameLength)>0">
          <xsl:value-of select="substring($string, $startPosParm+2,$parmNameLength)"/>
        </xsl:when>
        <xsl:otherwise/>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="$parm"/>
  </xsl:template>
</xsl:stylesheet>

You might think to just loop over the properties in the property list and then replace all the corresponding properties with the corresponding values. But that won't work. You can't repeatedly change a variable or target in XSL. The trick to solve this is recursion.

The template first looks up the email text, in the "getEmailText" template, using the same lookupXML function as used in my previous post. Then it replaces all occurences of properties from the template text using the "replaceProps" template. That template does a lookup of the first occurence of the parameter within the text, using the "getFirstProp"-template. If it finds one, it will do a replace of the property with the value and feed the result recursively into the "replaceProps"-template. The value is queried using the key from the propertylist  If it does not find a property then it will return just the text, which ends the recursion.

The lookup and replacement is done with about the same functions I described in this post. Unfortunately I found a bug in the replace template in that post.  I solved it in the version above, where I also left-out a small un-needed functionality. Maybe you can find it as well?

I won't explain the rest of the process. After the transformation above it just fills in the blanks in the notification service using the elements above. The property list is a simle name-value-pair list. To fill that is not so hard either.

LookUp DVM in SoaSuite 10g, the 11g way

In SoaSuite 10g there is a xpath and xslt function do Domain Value Map lookups,lookupDVM. This function is based on tables in the SoaSuite repository. There is a screen in the ESB console to edit them.

In SoaSuite 11g there is a separate artefact for DVM's, and a corresponding lookupDVM function.
I found that one more convenient, since there is an editor in Jdeveloper for it and the artefact is in fact an xml file that can be put in the MDS and referenced from anywhere in your SOA projects. And lastly, it can be versioned as any other artefact.
Deployment is similar to other SOA-artefacts. For 10g you must ensure to synchronise the dvm's in the database between the different (OTAP) environments.

Today I needed a simple translation from 0,1 to Ja,Nee (Yes, No). I did not feel like to create a DVM in the database. But, also in 10g there is a quite similar solution as the 11g lookupDVM function

In SoaSuite 10g, there is an oracle xsl-extension function: lookupXML. It enables you to read in a xml file and perform a xpath-lookup query. I found it very convenient to lookup fixed (OTAP) environment-dependent parameters. If you would put those in bpel preferences you have to ensure that they're changed every deployment between the environments. Using the lookupXML function, you can lookup those values from an xml file from the filesystem of the SOASuite. Then you only have to put an version of the file in a fixed folder on every SOASuite environment. After adapting those for the particular environment, you don't need to touch it every deployment anymore.

This same construction can be used to do lookups. I created the following xml file:

<?xml version="1.0" encoding="windows-1252" ?>
<!-- $Id: email_texts.xml 99 2013-02-01 08:27:07Z ca_mavandenakker $ -->
<!-- This file can be located in a HTTP accessible directory in the midtier installation and it's referred 
     through an lookup-xml XPath expression in the service invocation.  -->
<!-- The current location is http://${deploy.soasuite.URL}/ObjectLibrary/Core/xml/v1/CoreLookupDVM.xml -->
<domains>
      <domain name="JaNee">
            <keyDb>0</keyDb>
            <dispValue>Nee</dispValue>
            <boolean>false</boolean>
      </domain>
      <domain name="JaNee">
            <keyDb>1</keyDb>
            <dispValue>Ja</dispValue>
            <boolean>true</boolean>
      </domain>
</domains>
   
Since the lookupXML is a xsl function, it is in 10g apparently not available as an Xpath function, I created an XSD for the convenience of doing the DVM-lookups:
<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:version="1.0"
            xmlns:svnId="$Id$" xmlns="http://www.darwin-it.nl/xsd/v1/DVM"
            xmlns:dvm="http://www.darwin-it.nl/xsd/v1/DVM"
            targetNamespace="http://www.darwin-it.nl/xsd/v1/DVM"
            elementFormDefault="qualified">
  <xsd:element name="DVM" type="dvm:DVMType">
    <xsd:annotation>
      <xsd:documentation>Domain Value Mapping</xsd:documentation>
    </xsd:annotation>
  </xsd:element>
  <xsd:complexType name="DVMType">
    <xsd:sequence>
      <xsd:element name="Domain" type="xsd:string"/>
      <xsd:element name="FromKey" type="xsd:string"/>
      <xsd:element name="FromValue" type="xsd:string"/>
      <xsd:element name="ToKey" type="xsd:string"/>
      <xsd:element name="ToValue" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>
Based on this XSD you can create a variable, fill in the Domain, eg. "JaNee", the FromKey, eg. "keyDb", the FromKeyValue, eg. "0", and the ToKey, eg. "dispValue" or "boolean". Then you can transform the variable to it's self using the following XSL:
<?xml version="1.0" encoding="UTF-8" ?>
<?oracle-xsl-mapper <!-- SPECIFICATION OF MAP SOURCES AND TARGETS, DO NOT MODIFY. -->
  <mapSources>
    <source type="XSD">
      <schema location="http://melia03.hhdelfland.nl/ObjectLibrary/Core/xsd/v1/DVM.xsd"/>
      <rootElement name="DVM" namespace="http://www.darwin-it.nl/xsd/v1/DVM"/>
    </source>
  </mapSources>
  <mapTargets>
    <target type="XSD">
      <schema location="http://melia03.hhdelfland.nl/ObjectLibrary/Core/xsd/v1/DVM.xsd"/>
      <rootElement name="DVM" namespace="http://www.darwin-it.nl/xsd/v1/DVM"/>
    </target>
  </mapTargets>
  <!-- GENERATED BY ORACLE XSL MAPPER 10.1.3.5.0(build 090730.0200.1754) AT [FRI FEB 01 10:04:09 CET 2013]. -->
?>
<xsl:stylesheet version="1.0"
                xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
                xmlns:ehdr="http://www.oracle.com/XSL/Transform/java/oracle.tip.esb.server.headers.ESBHeaderFunctions"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:version="1.0"
                xmlns:ns0="http://www.darwin-it.nl/xsd/v1/DVM"
                xmlns:hwf="http://xmlns.oracle.com/bpel/workflow/xpath"
                xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20"
                xmlns:xref="http://www.oracle.com/XSL/Transform/java/oracle.tip.xref.xpath.XRefXPathFunctions"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ora="http://schemas.oracle.com/xpath/extension"
                xmlns:ids="http://xmlns.oracle.com/bpel/services/IdentityService/xpath"
                xmlns:orcl="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc"
                xmlns:svnId="$Id$"
                exclude-result-prefixes="xsl xsd version ns0 svnId bpws ehdr hwf xp20 xref ora ids orcl">
  <xsl:variable name="configURI"
                select="'http://${deploy.soasuite.URL}/ObjectLibrary/Core/xml/v1/CoreLookupDVM.xml'"/>
                
  <xsl:template match="/">
    <ns0:DVM>
      <ns0:Domain>
        <xsl:value-of select="/ns0:DVM/ns0:Domain"/>
      </ns0:Domain>
      <ns0:FromKey>
        <xsl:value-of select="/ns0:DVM/ns0:FromKey"/>
      </ns0:FromKey>
      <ns0:FromValue>
        <xsl:value-of select="/ns0:DVM/ns0:FromValue"/>
      </ns0:FromValue>
      <ns0:ToKey>
        <xsl:value-of select="/ns0:DVM/ns0:ToKey"/>
      </ns0:ToKey>
      <ns0:ToValue>
        <xsl:call-template name="LookupDVM">
          <xsl:with-param name="domainName" select="/ns0:DVM/ns0:Domain"/>
          <xsl:with-param name="fromKey" select="/ns0:DVM/ns0:FromKey"/>
          <xsl:with-param name="fromKeyValue" select="/ns0:DVM/ns0:FromValue"/>
          <xsl:with-param name="toKey" select="/ns0:DVM/ns0:ToKey"/>
        </xsl:call-template>
      </ns0:ToValue>
    </ns0:DVM>
  </xsl:template>
  <!--  User Defined Templates  -->
  <xsl:template name="LookupDVM">
    <xsl:param name="domainName"/>
    <xsl:param name="fromKey"/>
    <xsl:param name="fromKeyValue"/>
    <xsl:param name="toKey"/>
    <xsl:variable name="parentPath"
                  select="concat('/domains/domain[@name=&quot;',$domainName,'&quot;]' )"/>
    <xsl:variable name="result"
                  select="orcl:lookup-xml($configURI,$parentPath,$fromKey,$toKey ,$fromKeyValue)"/>
    <xsl:value-of select="$result"/>
  </xsl:template>
</xsl:stylesheet> 
This for those cases that you just want to lookup some values from the domain-xml this construction can be handy. In other cases where you need to lookup multiple values in an complexer xsl't you can copy and paste the construction above.

Since the CoreLookupDVM.xml is a, quite simple, xml file, it can easily be transformed to an 11g lookupDVM file.