Saturday, 19 November 2011

Custom XPath functions in Oracle SOA Suite 11g

In this post I am going to show how to create a custom XPath function for XSLTs and BPELs in Oracle SOA Suite 11g.We can create custom XPath functions for XSLT,BPEL,Mediator and Human Workflow. However the process of creating the function is quite different for XSLT compared to that for other components.First I shall discuss the steps for creating a custom XPath function for XSLTs.To illustrate the process, I am going to write a simple "hello world" XPath function.The steps for creating the XPath function are :-

1.Create a java class and implement your method.
2.Create a configuration file that describes the XPath function and links it to the java method.
3.Add the function at design time in JDev.
4.Make the function available at run time. 

So, lets get started.Open JDev and  create a new generic application and a new generic project.Now create a java class.Following is the sample java class that I used.The java class contains two methods, a static one and a non-static one.The methods that implements the logic for an XPath function can be static or non-static.If the method is non-static,the process of invoking the function is slightly different which is illustrated in the following sections.

package testjavacall;
public class TestClass{

    public static String getGreeting(String param){
        return "hello "+param;
       
    }
   
    public String helloWorld(String str){
      return "hello "+str;
    }
   
}

Now, create a directory named "META-INF"  inside the "src" folder for your project.(e.g. C:\JDeveloper\mywork\Test\TestJavaCall\src).Now in JDev, create a new XML document named
"ext-mapper-xpath-functions-config.xml" and put it inside the "META-INF"  directory that you just created inside the "src".Your directory structure should look like the following  image:-




The name of the configuration file must be followed.For XSLTs, the name must be "ext-mapper-xpath-functions-config.xml".For other components, the name of the file is different.

Oracle BPEL Process Manager :               ext-bpel-xpath-functions-config.xml
Oracle Mediator:                                       ext-mediator-xpath-functions-config.xml
XSLT Mapper:                                          ext-mapper-xpath-functions-config.xml
Human workflow:                                      ext-wf-xpath-functions-config.xml
All components                                          ext-soa-xpath-functions-config.xml

Following is the contents of the configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<soa-xpath-functions version="11.1.1"
                     xmlns="http://xmlns.oracle.com/soa/config/xpath"
                     xmlns:sample="http://www.oracle.com/XSL/Transform/java/testjavacall.TestClass">
  <function name="sample:getGreeting">
    <className>testjavacall.TestClass</className>
    <return type="string"/>
    <params>
      <param name="str" type="string"/>
    </params>
    <desc>hello world method</desc>
  </function>
  
  <function name="sample:helloWorld">
    <className>testjavacall.TestClass</className>
    <return type="string"/>
    <params>
      <param name="str" type="string"/>
    </params>
    <desc>non-static hello world method</desc>
  </function>
</soa-xpath-functions>

There are a few things to notice about the configuration file.

  • Notice that we have declared a namespace "sample".You could choose any name but the point is this prefix should be used in your XSL file while referring to the function.The namespace URI must have the follwing format:     http://www.oracle.com/XSL/Transform/java/<<fully qualified class name having packages>> .So in this case, the namespace URI takes the form: http://www.oracle.com/XSL/Transform/java/testjavacall.TestClass.
  •  In the parameter and return type function, we have specified string.The methods that implement the logic for the XSL function (i.e. getGreeting and helloWorld in this case) must return one of the following types:
                java.lang.String
                int,float,double,boolean
                oracle.xml.parser.v2.XMLNodeList
                oracle.xml.parser.v2.XMLDocumentFragment


                 Corresponding to these java types, in the configuration file, we can specify the following types:

                    string for java.lang.String
                    number for int,float,double
                    boolean for boolean
                    node-set for  oracle.xml.parser.v2.XMLNodeList
                    tree for oracle.xml.parser.v2.XMLDocumentFragment

Now create a jar file.Our next step is to add the function  at design time in JDev.To do this, go to tools->preferences->SOA and add the jar file that you just created.Now restart JDev.Then create a new XSL Mapper file in your project.Now open the xsl file, and go to component palette.The functions helloWorld and getGreeting should be available under the user defined section as shown in the following image.




We can use the getGreeting function in the same way we use other XPath functions.However, the java method helloWorld is not a static one.So, we have to do a bit more work to invoke this function.To invoke a non-static function, we must instantiate the class in our xsl file and pass the instance of that class as the first parameter to the function.The following code snippet illustrates this:-
                 
<?xml version="1.0" encoding="UTF-8" ?>
               <!-- some processing instructions -->
<xsl:stylesheet version="1.0" ..........  xmlns:sample="http://www.oracle.com/XSL/Transform/java/testjavacall.TestClass">

                        ..............................

                            <!-- Instantiate the class -->
                               <xsl:variable="obj" select="sample:new()"/>
                                <xsl:value-of select="sample:helloWorld($obj,'World')"/>
                                
                         .............................
 </xsl:stylesheet>

Please note that this code snippet may be showing errors in the design view but when tested, it works fine.

Now the next step is to test our custom functions which we can do using the  built in testing functionality.To do this,right click on an empty space on the mapper file and select test.

The next step is to make the function available at run time.To do this, place the jar file under:
    <<MW_HOME>>/user_projects/domains/<<domain_name>>/lib or under a sub-directory of lib.You should restart the weblogic server now.


  For BPEL, Mediator and Human Workflow, this process is quite different.The class that contain the method must implement the interface oracle.fabric.common.xml.xpath.IXPathFunction.This interface has single method.The signature of he method is :

                 public Object call(oracle.fabric.common.xml.xpath.IXPathContext ctx,java.util.List params)

The call method is invoked whenever we invoke our function from a BPEL or mediator assign activity.Follow the same steps as before and create a generic application and a generic project.Then create a META-INF directory inside "src" directory of your project space.Now, we should add the libraries needed.To do this, right click on your project, go to project properties->libraries and classpath and add the following libraries:-

               SOA Designtime
               SOA Runtime
               MDS Runtime
               BPEL Runtime
               Mediator Runtime

Next we shall proceed with writing the  java class.The sample java class is as follows:-

package xathbpelfunction;
import java.util.List;
import oracle.fabric.common.xml.xpath.IXPathContext;
import oracle.fabric.common.xml.xpath.IXPathFunction;
public class UtilityFunctions implements IXPathFunction{
   
    public Object call(IXPathContext context,List args) {
     
      String str=(String)args.get(0);
      return processString(str);
     
    }
   
     private String processString(String str){
      if(str==null)
          return null;
      return "hello "+str;
    }
}










Now create an XML file named "ext-soa-xpath-functions-config.xml"  and put it under the META-INF directory that you created.The source of the file is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<soa-xpath-functions version="11.1.1"
                     xmlns="http://xmlns.oracle.com/soa/config/xpath"
                     xmlns:func="http://test.functions.com">
                    
  <function name="func:test">
    <className>xathbpelfunction.UtilityFunctions</className>
    <return type="string"/>
    <params>
      <param name="str" type="string"/>
    </params>
    <desc>hello world method</desc>
  </function>
 
</soa-xpath-functions>





First notice that the namespace func has an arbitrary URI.Unlike the previous case where we must follow a specific format,here we can use any URI that we wish.



Next, notice that there is no method named test in out java class.Here the method 'call' is going to be invoked automatically when the function is invoked.Now create a jar file and follow the same steps as before to add the function in JDev at design time.Also, place the jar file in the   <<MW_HOME>>/user_projects/domains/<<domain_name>>/lib or under a sub-directory of lib and restart the weblogic server.



Now create a SOA project and add a BPEL process.Then drag an assign activity.Open the assign and select copy operation.Then bring up the expression builder and select user defined extension function from the drop down menu.The function test should appear.




You can now use it like any other function.Note that as we named our configuration file "ext-soa-xpath-functions-config.xml", it should also be visible in mediator and human work flows.

Hope this helps. 

4 comments:

  1. Excellent, Images are not diplaying in article. Please fix

    ReplyDelete
  2. Hi....when i do the Test the XSLT from JDev...i am getting error saying that that the function not found.
    i have included the jar file in both the Project Library and Tools-Preferences->SOA

    please mail me @ sanju03@gmail.com...need to fix it asap.

    ReplyDelete
  3. If my call method return string .I'm getting javax.xml.xpath.XPathExpressionException: Cannot convert string to NodeSet.
    Incase of null its working fine.

    ReplyDelete