14 May 2010

Using Custom XPATH Functions


Custom XPath functions provide a great way of handling functionality which cannot be performed using standard set of available functions. Oracle SOA suite comes with many functions which include standard XPath functions as well as SOA and BPEL XPath extension functions. However, many times a situation comes where we wish that so and so function was available.
In such case, arguably custom XPath is the best way to deal with.

In the following steps, a very simple example is taken and implemented as custom XPath function which can be used inside BPEL processes  in a typical Oracle SOA Composite.

Example Discussed : A  function getSum which takes input two numbers are returns their sum.

Main steps are : 
1) Implementing the function logic ( using java.class )
2) Creating a jar file which contains the class and configuration details.
3) Associating the created function with JDeveloper.
4) Associating the function with SOA Suite.

Implementing the function logic

1) Oracle provide a interface "oracle.fabric.common.xml.xpath.IXPathFunction" which can be implemented to create custom classes
This is present in a jar file named "fabric-runtime.jar" which can be found at any of following locations:
<MIDDLEWARE_HOME>\jdeveloper\soa\modules\oracle.soa.fabric_11.1.1
<MIDDLEWARE_HOME>\Oracle_SOA1\soa\modules\oracle.soa.fabric_11.1.1

The logic for the function is incorporated in a java class by implementing this interface.

So in Jdeveloper, create a empty(generic) project. Go to project properties and in "Libraries and Classpath" add this jar file by clicking "Add Jar/Library"



 2) Create a new java class file in the project with name as getSum and package as my.custom.
Also select main method which we would use to test the class.


3) In the java class, implement the IXPathFunction interface and the method Call as shown below.
Also add the code in the main method for testing :





4) Test the code and make sure it works fine.

Creating jar file :

1) On your desktop (anywhere) create a new folder and inside this create a folder called code. In this folder create two folders src and META-INF
2) In src folder , copy the java file with correct package. In META-INF , we put the configuration file.
 A configuration file is the one which actually associates the Java class with the JDeveloper as well  as SOA Suite. Based upon the type of component, the name of the file differs.
For implementation in BPEL, name is ext-bpel-xpath-functions-config.xml (note that for XSLT also name if different)
3) Create a configuration file in META-INF folder with name as ext-bpel-xpath-functions-config.xml. Add following to file:




4) In the main folder, where have two folders - src and META-INF , create a build file (build.xml) which will have tasks for compiling the java code and creating a jar file
The jar file must have the META-INF folder in it along with the class files. A sample build file which I used is shown below:


 5) Run ant and create the Jar file.

 Associating the created function with JDeveloper :

1) Place the jar file at some standard place on your machine.
2) In JDeveloper, open Tools > Preferences and go to SOA. Add the jar file.


3) Restart the JDeveloper.
4) Create a new SOA project with BPEL , add assign activity.
In the assign activity, create a copy operation and select Expression on input side. In expression builder select "User Defined Extension Functions".The function will appear like this :


5) Function has signature mf:myFunction1() and can be used like: mf:myFunction1("10","20" )  (Note the double quotes around the  numbers)
6) Use the function , save and compile the project. While saving, it might show blue line below the function signature, ignore it.

Associating the function with SOA Suite :

1. Go to the location <MIDDLEWARE_HOME>\user_projects\domains\<DOMAIN_NAME>\lib and put the jar file here.
2. Restart the server. On restarting the Weblogic server, the command prompt log will show a message that the jar file is picked up.
3. Deploy the project in JDeveloper and Test.



About the configuration file :

As described above,for using custom XPath functions, we need to have a configuration file. The details about this file can be found in Developer's Guide for SOA suite.
However, for sake of  completeness of article, the points are discussed in brief :
1. This file is a XML file and needs to be placed in a folder named META-INF immediately inside the jar file.
2. Name of the file depends on the the usage as below : (Reference : Table B2 in Developer's Guide)

Name of the component Name of  config file
BPEL Process  ext-bpel-xpath-functions-config.xml
Mediator  ext-mediator-xpath-functions-config.xml
Transformations  ext-mapper-xpath-functions-config.xml
Human workflow  ext-wf-xpath-functions-config.xml
Generic (All compoents)  ext-soa-xpath-functions-config.xml


3. The file must follow the  schema given in the Developer's guide.

4. Following are the main elements in the file: (Reference : Table B1 in Developer's Guide)

Element Description
className Name of the main class which implements the function logic
return Return type. Possible values are string, number, boolean, node-set, tree.
params list of input arguments for function. There is one line per argument . Each parameter specifies the details like name, type using attributes


A POC for the above example be downloaded from here.

4 comments:

SureshChandra G said...

thank you for the post. I have been looking for this for a long time now

Ketan said...

Thanks SureshChandra.

Keith said...

This works well in jDeveloper but I cannot get it to integrate with the SOA server.

Other blogs discuss putting the jar in \server_name\soa\modules\oracle.soa.ext_11.1.1 and updating the clas-path in oracle.soa.ext.jar with the name of the new jar.

At this point; I followed both directions!

I do see my .jar being picked up in the log (only after putting it where you said to put it) but I still get a function not found error at run time.

I'm flat out of ideas here. :(

My class is very similar to yours.. (reproduced below)

package com.f17.customfunction;

import java.util.LinkedList;
import java.util.List;

import oracle.fabric.common.xml.xpath.IXPathContext;
import oracle.fabric.common.xml.xpath.IXPathFunction;
import oracle.fabric.common.xml.xpath.XPathFunctionException;

public class editCaseNumber implements IXPathFunction {

/* public static void main(String [] args) throws XPathFunctionException {
editCaseNumber editCaseNumber = new editCaseNumber();
IXPathContext IXPathContext = null;
List l = new LinkedList();
l.add(args[0]);
l.add(args[1]);
Object o = editCaseNumber.call(IXPathContext, l);
System.out.println(o.toString());
}
*/

public editCaseNumber(){
super();
}

public Object call(IXPathContext context, List args) throws XPathFunctionException {
String stringToFormat = (String) args.get(0);
String formatMask = (String) args.get(1);
String outOne = getFormattedString.normalize(stringToFormat,formatMask);
return outOne;
}
}

Ketan said...

In your code snippet, the variable getFormattedString is not defined. But i commented that call and the class is working fine. BPEL process was able to get the output from this class. May be you can add sop statements and verify if it is printed in the logs.