Sunday, 10 February 2013

Securing SOA 11g Environment:Part 2

This post continuation of my previous one http://shrikworld.blogspot.in/2011/07/securing-soa-11g-environment.html entry.So I’ve SSL enabled admin and managed server and I want to start all managed server from node manager, that requires some tweak in nodemanager.properties file. Below is the sample one,

#Fri Feb 08 20:18:43 IST 2013
#Sun Oct 28 16:39:51 IST 2012
DomainsFile=C\:\\shrik\\SOA11116\\WLSERV~1.3\\common\\NODEMA~1\\nodemanager.domains
LogLimit=0
PropertiesVersion=10.3
DomainsDirRemoteSharingEnabled=false
javaHome=C\:\\PROGRA~1\\Java\\JROCKI~1.0
AuthenticationEnabled=true
NodeManagerHome=C\:\\shrik\\SOA11116\\WLSERV~1.3\\common\\NODEMA~1
JavaHome=C\:\\PROGRA~1\\Java\\JROCKI~1.0\\jre
LogLevel=INFO
DomainsFileEnabled=true
StartScriptName=startWebLogic.cmd
ListenAddress=
NativeVersionEnabled=true
ListenPort=5556
LogToStderr=true
SecureListener=true
LogCount=1
DomainRegistrationEnabled=true
StopScriptEnabled=false
QuitEnabled=true
LogAppend=true
StateCheckInterval=500
CrashRecoveryEnabled=false
StartScriptEnabled=true
LogFile=C\:\\shrik\\SOA11116\\WLSERV~1.3\\common\\NODEMA~1\\nodemanager.log
LogFormatter=weblogic.nodemanager.server.LogFormatter
ListenBacklog=50
KeyStores=CustomIdentityAndCustomTrust
CustomIdentityAlias=shrikistore
CustomIdentityKeyStoreFileName=C:\\shrik\\SOAWork\\WeblogicCerts\\shrikIS.jks
CustomIdentityKeyStorePassPhrase=<Enter keysrote password>
CustomIdentityKeyStoreType=JKS
CustomIdentityPrivateKeyPassPhrase=<Enter private key password>

At runtime node manager will encrypt the hard coded pass phrase.Check the node manager status from weblogic console , it should be running fine and reachable.

In last post we disabled host name verification in SSL configuration of weblogic which is not advisable for production box.Ideally the certificate issued to weblogic server should match the host name where it’s running. For simplicity check out my cert configuration for SSL enablement of Admin and managed server.

image

Though leaf cert’s CN Shreekanta does not match  hostname localhost , it will fail definitely at Host name checker if I try to  open Admin console from browser which acts as a client.So in my custom hostname verifier code I’ve to add my cert’s CN validation and allow access. For that you have to select custom hostname verifier like below and you need to key in the class name start from your custom package,

image

My code is like below [copied from Oracle site] where I allowed My CN’S request,

package com.shrik.weblogic;

import java.io.ByteArrayInputStream;

import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.SSLSession;

public class TestHostnameVerifier implements weblogic.security.SSL.HostnameVerifier {

    private final static Logger log = Logger.getLogger(TestHostnameVerifier.class.getCanonicalName());

    @Override
    public boolean verify(String hostname, SSLSession session) {


        try {
            Certificate cert = session.getPeerCertificates()[0];

            byte[] encoded = cert.getEncoded();

            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            ByteArrayInputStream bais = new ByteArrayInputStream(encoded);

            X509Certificate xcert = (X509Certificate)cf.generateCertificate(bais);

            String cn = getCanonicalName(xcert.getSubjectDN().getName());

            log.info("CN: '" + cn + "'" + " HOSTNAME: '" + hostname + "'");


            // If CN is equals to Hostname then it is approved
           //added my cert
           if (cn.equals(hostname)||(cn.equals("Shreekanta"))) {
                return true;
            }

            // Compile regular expression
            // here we set the wildcard and the rest of the URL inside, could be more generic too.
            // String patternStr = cn;//  "[-.*aA-zZ0-9]+\\.qa.go2broadband\\.com";
            cn = cn.replace(".", "\\.");
            cn = cn.replace("-", "\\-");
            cn = cn.replace("*", "[-.*aA-zZ0-9]+");

            Pattern pattern = Pattern.compile(cn);

            // Determine if there is an exact match
            CharSequence inputStr = hostname;
            Matcher matcher = pattern.matcher(inputStr);
            boolean matchFound = matcher.matches();

            if (matchFound == false) {
                log.info("pattern doesn't match hostname");
            }

            //return boolean value
            return matchFound;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return true;
    }

    /**
     * Returns just the canonical name from the distinguishedName on the cert.
     *
     *
     */
    private String getCanonicalName(String subjectDN) {

        Pattern pattern = Pattern.compile("CN=([-.*aA-zZ0-9]*)");
        Matcher matcher = pattern.matcher(subjectDN);

        if (matcher.find()) {
            return matcher.group(1);
        }

        log.info("Couldn't find match for CN in subject");
        return subjectDN;

    }

}


Do jar it and place it at <Domain_Home>/lib directory and <WL_HOME>/server/lib directory. At setDomainEnv.sh file add the following entry,

set CLASSPATH=%WL_HOME%\server\lib\HostNameVerifier.jar;%CLASSPATH%

Check out the order, for my case if I add custom jar file at end of classpath it always give me error and add the below entry ar server startup to debug.

set JAVA_OPTIONS=%JAVA_OPTIONS% -Djavax.net.debug=all -Dssl.debug=true -Dweblogic.StdoutDebugEnabled=true

That’s all , bounce the serer and verify the same from any browser accessing https URL of server.

Sunday, 23 September 2012

How to FTP flat file from BPEL

Recently I had a requirement, I’ve to publish a flat file which is position based to FTP server from BPEL.But the problem is I don’t have any nXSD/DTD to use in FTP adapter.Client will pick the file from FTP location based on image of file content.I first tried with opaque option in FTP adapter in vain because from BPEL I’m generating the whole data in String format and opaque require base64 encoding.Another constraint is that if I publish any file with a predefined schema then the actual content will be enclosed by XML tag which i don’t want. So here is my solution…

At first download ftp4j jar from http://www.sauronsoftware.it/projects/ftp4j/ which makes your life more easier.In BPEL I’ve the data in string format , I’ve to convert the same in a file content , then need to put the same in FTP server.

Place the ftp4j file in SCA-INF/lib folder of your BPEL project, obviously I’m using SOA11.1.1.5.So when you compile or deploy the project it will be referenced in the classpath.You need not to place that jar anywhere else :)

Then add the following import in your .bpel file,

<bpelx:exec import="java.util.*"/>
<bpelx:exec import="java.lang.*"/>
<bpelx:exec import="it.sauronsoftware.ftp4j.FTPClient"/>
<bpelx:exec import="it.sauronsoftware.ftp4j.FTPFile"/>
<bpelx:exec import="java.io.File"/>
<bpelx:exec import="java.io.FileWriter"/>
<bpelx:exec import="oracle.xml.parser.v2.XMLElement"/>

Here is the code which will FTP your file,

try{    
                String FTP_SERVER="XXXX";    
                String USRE_NAME="YYY";    
                String PASS="ZZZ";    
                String PDATA =(String)getVariableData("filePDATA");  
                addAuditTrailEntry(PDATA);   
                XMLElement fileIn=(XMLElement)getVariableData("SelectPDATAOfFileDB_InputVariable","SelectPDATAOfFileDBInput_msg","/ns6:SelectPDATAOfFileDBInput/ns6:FILE_NAME");
                addAuditTrailEntry(fileIn.getTextContent()); 
                FTPClient client=new FTPClient();  
                client.connect(FTP_SERVER);  
                client.login(USRE_NAME,PASS);  
                System.out.println("Connected to server");  
                addAuditTrailEntry("Connected to FTP server");   
                File makefile=new File(fileIn.getTextContent());  
                FileWriter fwrite = new FileWriter(makefile);  
                fwrite.write(PDATA);  
                fwrite.flush();  
                fwrite.close();  
                client.upload(makefile);  
                client.logout();  
                client.disconnect(true);   
            }catch(Exception e){    
                System.out.println(e.getMessage());    
                addAuditTrailEntry(e.getMessage());     
            }

Here you have to provide your FTP server URL , username and password.You can create 3 preference variable and refer the same using getVariableData from Java.In my program filePDATA variable store file content in BPEL and I’m also passing the filename as input to my program and at last using the method of FTPClient of ftp4j library I’m placing the file in remote server.As the file content is String , so you have full control on that , I mean you can manipulate data as you need. 

Looks so simple , but took some amount of time to figure out :)

Sunday, 27 May 2012

Configure SSL in FMW DataSource

Few days back I was looking into how to enable SSL in datasource and database.I didn’t find any good document with step by step instruction, however at last I was able to configure it successfully after reading a whitepaper at http://www.oracle.com/technetwork/database/enterprise-edition/wp-oracle-jdbc-thin-ssl-130128.pdf.

NOTE :SSL can not be configured in XE database.In this blog I mentioned the path as per my system , you need to configure accordingly.

Enabling SSL in DB:

First create a directory under C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\BIN\owm\wallets\test where I'll place ewallet.p12.

orapki wallet create -wallet C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\BIN\owm\wallets\Shreekanta -auto_login

image

that will create ewallet.p12.Open OWM and wallet,

image

Just need to add a cert here,

image

image

Now basically you need to export CSR and submit to CA.

image

For my case I've used free CA available over net at http://www.getacert.com/signacert.html. Upon submitting CSR it will give CAcert and test cert.so here is my directory structure,[refer http://shrikworld.blogspot.in/2011/07/securing-soa-11g-environment.html]

image

Now we need to enter CA into trust store of wallet and test cert into certificate branch like below,

image

image

After that wallet should show cert as ready, save and exit the same.

image

Now we need to modify the following files accordingly,

 

listener.ora

# listener.ora Network Configuration File: C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\network\admin\listener.ora

# Generated by Oracle configuration tools.

SID_LIST_LISTENER =

  (SID_LIST =

    (SID_DESC =

      (SID_NAME = CLRExtProc)

      (ORACLE_HOME = C:\shrik\Oracle\Database\product\11.2.0\dbhome_1)

      (PROGRAM = extproc)

      (ENVS = "EXTPROC_DLLS=ONLY:C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\bin\oraclr11.dll")

    )

  )

LISTENER =

(DESCRIPTION_LIST =

    (DESCRIPTION =

      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))

      (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))

                          (ADDRESS = (PROTOCOL = TCPS)(HOST = localhost)(PORT = 2490))

    )

  )

ADR_BASE_LISTENER = C:\shrik\Oracle\Database

WALLET_LOCATION = (SOURCE = (METHOD=file) (METHOD_DATA=(DIRECTORY=C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\BIN\owm\wallets\Shreekanta)))

SSL_CLIENT_AUTHENTICATION=FALSE

tnsnames.ora

# tnsnames.ora Network Configuration File: C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\network\admin\tnsnames.ora

# Generated by Oracle configuration tools.

LISTENER_ORCL =

  (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))

ORACLR_CONNECTION_DATA =

  (DESCRIPTION =

    (ADDRESS_LIST =

      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))

    )

    (CONNECT_DATA =

      (SID = CLRExtProc)

      (PRESENTATION = RO)

    )

  )

ORCL =

  (DESCRIPTION =

    (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))

    (CONNECT_DATA =

      (SERVER = DEDICATED)

      (SERVICE_NAME = orcl)

    )

  )

  SSL =

  (DESCRIPTION =

    (ADDRESS_LIST =

      (ADDRESS = (PROTOCOL = TCPS)(HOST = localhost)(PORT = 2490))

    )

    (CONNECT_DATA =

      (SERVICE_NAME = orcl)

    )

    (SECURITY=(SSL_SERVER_CERT_DN=\"CN=test,C=IN\"))

  )

sqlnet.ora

# sqlnet.ora Network Configuration File: C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\network\admin\sqlnet.ora

# Generated by Oracle configuration tools.

# This file is actually generated by netca. But if customers choose to

# install "Software Only", this file wont exist and without the native

# authentication, they will not be able to connect to the database on NT.

SQLNET.AUTHENTICATION_SERVICES= (BEQ,TCPS,NTS)

NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)

WALLET_LOCATION = (SOURCE = (METHOD=file) (METHOD_DATA=(DIRECTORY=C:\shrik\Oracle\Database\product\11.2.0\dbhome_1\BIN\owm\wallets\Shreekanta)))

SSL_CLIENT_AUTHENTICATION=FALSE

 

Then restart the DB and listener and your database is now SSL enabled over TCPS.

Configuring SSL in DataSource of FMW:

Add the certs that you generated in above steps in weblogic trust store.[please refer http://shrikworld.blogspot.in/2011/07/securing-soa-11g-environment.html]

restart weblogic server.Go to the datasource,

Click on connection pool,  add the url as below,

jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCPS)(HOST=localhost)(PORT=2490)))(CONNECT_DATA=(SERVICE_NAME=orcl))(SECURITY=(SSL_SERVER_CERT_DN="CN=test,C=IN")))

In the properties section add the below property accordingly and test

javax.net.ssl.trustStorePassword=Password

user=DEV_MDS

javax.net.ssl.trustStore=C:\shrik\Oracle\fmw11.1.1.5\wlserver_10.3\server\lib\shrikTrust.jks

javax.net.ssl.trustStoreType=JKS

 

Test the same.

Now the datasource is SSL enabled.

Saturday, 31 December 2011

XPATH in OSB

In this blog I’ll show you some useful XPATH expression in OSB, hope it would reduce lot of searching time if you are new to this subject.To get started I’ll use below XSD in OSB project.

<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.oracle.com"
            targetNamespace="http://www.oracle.com"
            elementFormDefault="qualified">
  <xsd:element name="PurchaseOrder">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="date" type="xsd:string"/>
        <xsd:element name="custID" type="xsd:string"/>
        <xsd:element name="items">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="item" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="description" type="xsd:string"/>
                    <xsd:element name="department" type="xsd:string"/>
                    <xsd:element name="price" type="xsd:float"/>
                    <xsd:element name="quantity" type="xsd:integer"/>
                  </xsd:sequence>
                  <xsd:attribute name="id" type="xsd:string"/>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="cardDetails">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="type" type="xsd:string"/>
              <xsd:element name="cardNumber" type="xsd:string"/>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
      <xsd:attribute name="id" type="xsd:integer"/>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
and here is the corresponding xml generated out of that above XSD.

<?xml version="1.0" encoding="UTF-8"?>
<p:PurchaseOrder id="1234" xmlns:p="http://www.oracle.com">
  <p:date>2010-04-02</p:date>
  <p:custID>C7123843</p:custID>
  <p:items>
    <p:item id="GF234324">
      <p:description>Denim Jeans</p:description>
      <p:department>Clothing</p:department>
      <p:price>30.99</p:price>
      <p:quantity>2</p:quantity>
    </p:item>
    <p:item id="HD312782">
      <p:description>iPod 80Gb White</p:description>
      <p:department>Electrical</p:department>
      <p:price>99.99</p:price>
      <p:quantity>1</p:quantity>
    </p:item>
    <p:item id="HD998775">
      <p:description>iPod Headphones</p:description>
      <p:department>Electrical</p:department>
      <p:price>19.99</p:price>
      <p:quantity>1</p:quantity>
    </p:item>
    <p:item id="KD123984">
      <p:description>Frying Pan</p:description>
      <p:department>Home</p:department>
      <p:price>9.99</p:price>
      <p:quantity>1</p:quantity>
    </p:item>
  </p:items>
  <p:cardDetails>
    <p:type>Mastercard</p:type>
    <p:cardNumber>1234-5678-1234-5678</p:cardNumber>
  </p:cardDetails>
</p:PurchaseOrder>

So here are the required scenarios and solution where actual path might be unknown in XML tree .

1.I want to retrieve the node where item id="HD998775.

Solution: $body//*:item[@id='HD998775']

2. I want to retrieve department name of 2nd item node.

Solution: data($body/*:PurchaseOrder/*:items/*:item[2]/*:department)

3.I want to retrieve department name of 4th item node using relative path.

Solution: data($body//*:item[4]/*:department)

So like above you can write any complex XPATH expression.

Sunday, 23 October 2011

Search & Replace JDBC Data source in XSLT using ANT

Hey everybody, today I come across a problem while deploying my code using ANT script.Each of my composite has its own deployment plan by which all the endpoint reference and jca properties getting changed to new environment.. but in the XSLT I’ve used couple of query database XPATH function and it use datasource as one of its input parameter.But I didn’t find any option in deployment plan to change the datasource as per my new environment.What if, we have a helper ant script that will search all the xsl files in a directory recursively and work in conjunction with main ant build script, definitely would be good. To get started I created a simple java class who will do the search and replacement job of all xslt files,

package com.shrik.utility;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class SearchAndReplaceXSLT {
    public static void main(String[] args) throws Exception {
        Properties properties=new Properties();
        properties.load(new FileInputStream("trx.properties"));
       
        File root = new File(properties.getProperty("FileRoot"));
       
        System.out.println(properties.getProperty("FileRoot"));
        String[] extensions = { properties.getProperty("FileExt") };
        boolean recursive = true;

        Collection files = FileUtils.listFiles(root, extensions, recursive);

        for (Iterator iterator = files.iterator(); iterator.hasNext(); ) {
            File file = (File)iterator.next();
            System.out.println("File = " + file.getAbsolutePath());
            SearchAndReplace(file,properties.getProperty("SearchStr"),properties.getProperty("ReplaceStr"));
        }
    }

   private static void SearchAndReplace(File file, String searchStr,
                                         String replaceStr) throws IOException {
        StringBuffer sb=new StringBuffer();
        Pattern pattern=Pattern.compile(searchStr);
        Matcher matcher=pattern.matcher(FileUtils.readFileToString(file));
        while(matcher.find()){
            matcher.appendReplacement(sb, replaceStr);
           
        }
        matcher.appendTail(sb);
        System.out.println(sb.toString());
        FileUtils.writeStringToFile(file,sb.toString() );
    }
}

and here is the corresponding trx.properties file,

FileRoot=C:/shrik/Oracle/SOAWork/composites/DeploymentFramework/test/trx
FileExt=xsl
SearchStr=DS_DEV_YYY&
ReplaceStr=DS_DEV_ZZZ&
build.dir=build
lib.dir=lib
main-class="com.shrik.utility.SearchAndReplaceXSLT"

Do fill up all the values based on your environment and root code repository directory.Just give a test run and check whether its doing your job or not.

Then create a jar file pointing your main class in manifest file.Here we have a dependency on org.apache.commons.io.FileUtils class so I created a folder lib and put the commons-io-1.3.2.jar file under that, here is my directory structure,

image

Under the build folder I created another directory jar , and under that I placed SearchAndReplaceXSLT.jar file.I created a trx folder where I copied all the xsl files, datasource need to be replaced.Now we need to refer the jar file from a ant script and for that I created build.xml file which contains,

<project name="SearchAndReplaceXSLT" basedir="." default="run">

<property file="trx.properties"/> 
    <property name="jar.dir"     value="${build.dir}/jar"/>
    <property name="classes.dir" value="${build.dir}/classes"/>
     <path id="classpath">
        <fileset dir="${lib.dir}" includes="**/*.jar"/>
    </path>
   
    <target name="run" >
        <java fork="true" classname="${main-class}">
            <classpath>
                <path refid="classpath"/>
                <path location="${jar.dir}/${ant.project.name}.jar"/>
            </classpath>
        </java>
    </target>

</project>

Now just run ant and it will do the job .

So next part is how will I call this ant file from main build.xml in deployment script?

In main build.properties file I created a new property trx.amendment=true . Based on this value it will call the child script as below,

<target name="deployAll">
        <if>
          <equals arg1="${trx.amendment}" arg2="true"/>
          <then>
             <ant antfile="${env.CURRENT_FOLDER}/trx.xml" inheritAll="false" target="run"/>
          </then>
      </if>     
       
         <if>
          <equals arg1="${mds.enabled}" arg2="true"/>
          <then>
             <antcall target="deployMDS" inheritall="true"/>
          </then>
      </if>     
      <foreach list="${applications}" param="application" target="deployApplication" inheritall="true" inheritrefs="false"/>
    </target>

So with this you can easily change any data source name in sequence or database query function of your XSLT.

Monday, 10 October 2011

Securing a BPEL composite using OEG

In this blog I’ll demonstrate how to secure a SOA composite using Oracle Enterprise Gateway with WS-Header token.At first deploy a simple HelloWorld service.

Follow the link to setup OEG in your system https://docs.google.com/leaf?id=1D0Z0KfgFdvqGHIcpG_7B32mWImGGCRkWhO882KyRVJFYAx94QK9yf6vVTjQP&hl=en

Start enterprise gateway and policy studio.

image

Click on enterprise gateway and edit the active configuration.Password for admin user is blank by default.

image

image

Now in policy tab register your service,

image

image

Select all the default option after that.After successful registration you will get a new generated circuit policy as below,

image

Now create a WS Header authentication policy in your policy library and set it as start.

image

Go to the user tab and create a new user as you given in previous step.

image

Now go back to your generated circuit and edit as below under Request from Client tab.

image

Deploy (F6) your configuration to Gateway.You are done with securing your service , now we’ll test it from service explorer.

Click on import wsdl and point your service endpoint , change the port to OEG port,

image

Under security token click on WS-Security username token and give the appropriate credentials as below,

image Now test the service and you should get HTTP 200 response.

image

Now login to traffic monitor of OEG and check your request,

image

Monday, 5 September 2011

Merge Two Source Element based on a Common Key in XSLT

I had a requirement where I need to merge the contents of two files into a single file and there was a common key between the two schema.We start our process by polling into first file location based on follow schema,

image

Then using synchronous read I’m reading second file whose content is based on below schema,

image

Now after merging I want the content structured as below where CustomerId is common key between two files,

image

and we have to do this merge in xslt , here is the simple code to achieve this,

image 

   <xsl:template match="/">
    <ns0:CustomerInfoFinal>
      <xsl:for-each select="/ns1:CustomerInfo1/ns1:Customer">
        <xsl:variable name="custID" select="ns1:CustomerId"/>
        <ns0:Customer>
          <ns0:CustomerId>
            <xsl:value-of select="ns1:CustomerId"/>
          </ns0:CustomerId>
          <ns0:Name>
            <xsl:value-of select="ns1:Name"/>
          </ns0:Name>
          <ns0:PAN>
            <xsl:value-of select="ns1:PAN"/>
          </ns0:PAN>
          <ns0:Address>
            <xsl:value-of select="$custDetails/ns2:CustomerInfo2/ns2:CustomerOtherInfo[(ns2:CustomerId = $custID)]/ns2:Address"/>
          </ns0:Address>
          <ns0:Age>
            <xsl:value-of select="$custDetails/ns2:CustomerInfo2/ns2:CustomerOtherInfo[(ns2:CustomerId = $custID)]/ns2:Age"/>
          </ns0:Age>
          <ns0:Gender>
            <xsl:value-of select="$custDetails/ns2:CustomerInfo2/ns2:CustomerOtherInfo[(ns2:CustomerId = $custID)]/ns2:Gender"/>
          </ns0:Gender>
        </ns0:Customer>
      </xsl:for-each>
    </ns0:CustomerInfoFinal>
  </xsl:template>