Wednesday, July 6, 2011

OSB executing XQuery defined in an external file

Let's admit it, support for reusability of XQuery code is EXTREMELY limited in OSB.
One has to play some trick.

One of them is using custom XPath function - defined in Java - and read and execute dynamically a XQuery, binding parameters dynamically.

Example:

This is the Java code:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;


static String BASE_DIR = "/path/to/config/osb/";

public static XmlObject trainWithParams(XmlObject[] parameters, String[] name) throws Exception {
String xquery = readFile(BASE_DIR + "train.xq"); 
XmlOptions options = new XmlOptions();
// see http://xmlbeans.apache.org/docs/2.2.0/reference/org/apache/xmlbeans/XmlOptions.html#setXqueryVariables%28java.util.Map%29
Map map = new HashMap();
for (int i = 0; i < name.length; i++) {
   map.put(name[i], parameters[i]);
  }
  options.setXqueryVariables(map);
  return runXQuery(xquery, options)[0];
}


private static XmlObject[] runXQuery(String xquery, XmlOptions options) {
  XmlObject xmlObject = XmlObject.Factory.newInstance();
  XmlObject[] results = xmlObject.execQuery(xquery, options);
  return results;
} 



private static String readFile(String fFileName) throws Exception {
  StringBuilder text = new StringBuilder();
  String NL = System.getProperty("line.separator");
  Scanner scanner = new Scanner(new FileInputStream(fFileName));
  try {
   while (scanner.hasNextLine()){
    text.append(scanner.nextLine() + NL);
   }
  }
  finally{
   scanner.close();
  }
  return text.toString(); 
}

This is the entry in the osb-built-in.xml

trainWithParams
Run a XQuery function returning XmlObject, passing many parameters
http://acme.com
com.acme.osb.XQueryExecutor
org.apache.xmlbeans.XmlObject trainWithParams([Lorg.apache.xmlbeans.XmlObject;, [Ljava.lang.String;)
true
Pipeline
SplitJoin
        




and this is how to invoke it from any XQuery context in OSB:

return acmxk:trainWithParams(($Train), ('Train'))


You can chain multiple parameters:

return acmxk:trainWithParams(($Train, $Bus), ('Train', 'Bus'))



Of course you can pass the Xquery file name in the call itself, by adding an extra parameter to the signature.

I am not sure if the XQuery will be recompiled every time, or it the XQuery engine keeps a cache. This mechanism exists in SQL engines, perhaps it's there also in XQuery engines.

Also I don't know if this compiled statement caching is in place for all XQuery defined in OSB.

No comments: