BDB XML allows you to define your own functions that you can access
from your XQueries. To do this, you must provide an implementation
of XmlExternalFunction
, and you must
implement a XmlResolver
class that resolves
which external function to call.
XmlExternalFunction
implementations only
require you to implement the execute()
method with your function code. You must also implement a
close()
method that cleans up after
whatever activities your execute()
method
calls.
The execute()
method offers three
parameters:
XmlTransaction
This is the transaction in use, if any, at the time the external function was called.
XmlManager
The XmlManager
instance in use
at the time the function was called.
XmlArguments
An array of XmlResults
objects
which hold the argument values needed by this function.
For example, suppose you wanted to write an external function that takes two numbers and returns the first number to the power of the second number. It would look like this:
package misc; import java.io.*; import java.lang.Math.*; import com.sleepycat.dbxml.*; /* External function pow() implementation */ class MyExternalFunctionPow extends XmlExternalFunction { public XmlResults execute(XmlTransaction txn, XmlManager mgr, XmlArguments args) throws XmlException { // Retrieve argument as XmlValue XmlResults argResult1 = args.getArgument(0); XmlResults argResult2 = args.getArgument(1); XmlValue arg1 = argResult1.next(); XmlValue arg2 = argResult2.next(); // Call pow() double result = Math.pow(arg1.asNumber(), arg2.asNumber()); // Create an XmlResults for return XmlResults results = mgr.createResults(); XmlValue va = new XmlValue(result); results.add(va); return results; } // The base class's close routine will call delete() by default. // In order to reuse the object it's necessary to override that with // a no-op version of close(). public void close() { } }
The XmlResolver
class is used to
provide a handle to the appropriate external function, when a given XQuery
statement requires an external function. For this reason, your
XmlResolver
implementation must have
knowledge of every external function you have implemented.
The resolver is responsible for instantiating an instance of the required external function. It is also responsible for destroying that instance, either once the query has been process or when the resolver instance itself is being destroyed. Which is the correct option for your application is an implementation detail that is up to you.
It is possible for your code to have multiple instances of an
XmlResolver
class, each instance of
which can potentially be responsible for different collections
of external functions. For this reason, you uniquely identify
each resolver class with a URI.
In order to call a specific external function, your XQueries must provide a URI as identification, as well as a function name. You can decide which external function to return based on the URI, the function name, and/or the number of arguments provided in the XQuery. Which of these are necessary for you to match the correct external function is driven by how many external functions you have implemented, how many resolver classes you have implemented, and how many variations on functions with the same name you have implemented. In theory, a very simple implementation could return an external function instance based only on the function name. Other implementation may need to match based on all possible criteria.
For the absolute most correct and safest implementation, you should match on all three criteria: URI, function name, and number of arguments.
For example, suppose you had two external functions:
SmallFunction
and
BigFunction
.
SmallFunction
is a small function that
requires few resources to instantiate and is called
infrequently. BigFunction
is a larger
function that opens containers, obtains lots of memory and from
a performance perspective is something that is best
instantiated once and then not destroyed until program
shutdown. Further, SmallFunction
takes two
arguments while BigFunction
takes five.
And XmlResolver
implementation for this
example would be as follows:
class MyFunResolver extends XmlResolver { private String uri_ = "my://my.fun.resolver"; XmlExternalFunction bigFunc = null; /* * Returns a new instance of either SmallFunction or * BigFunction if the URI, function name, and number of * arguments match. */ public XmlExternalFunction resolveExternalFunction(XmlTransaction txn, XmlManager mgr, String uri, String name, int numberOfArgs) throws XmlException { XmlExternalFunction fun = null; if (uri.equals(uri_) && name.equals("bfunc") && (numberOfArgs == 2)) { // bfunc is reusable. if(bigFunc == null) bigFunc = new BigFunction(); return bigFunc; } else if (uri.equals(uri_) && name.equals("sfunc") && (numberOfArgs == 1)) { fun = new SmallFunction(); } return fun; } public String getUri(){ return uri_; } public void close(){ if(bigFunc != null) bigFunc.delete(); } };
In order to use your external functions, you must register the
resolver that manages them. You do this with the
XmlManager.registerResolver()
method. You then set a URI prefix for the URI that you use to
identify your resolver. For example:
try { // Create an XmlManager XmlManager mgr = new XmlManager(); // Create an function resolver MyFunResolver resolver = new MyFunResolver(); // Register the function resolver to XmlManager mgr.registerResolver(resolver); XmlQueryContext context = mgr.createQueryContext(); // Set the prefix URI context.setNamespace("myxfunc", resolver.getUri());
To use the external function, declare them in the preamble of your query, and then use them as you would any XQuery function (for a complete explanation of examining query results, see the next section). For example:
declare function myxfunc:sfunc($a as xs:double, $b as xs:double) \ as xs:double external; myxfunc:sfunc(2,3);
You run this query as if you were running any other query.
String query1 = "declare function " + "myxfunc:sfunc($a as xs:double, $b as xs:double) as " + "xs:double external;\n" + "myxfunc:sfunc(2,3)"; XmlResults results = mgr.query(query, context); // The first query returns the result of sfunc(2,3) while (results.hasNext()) { XmlValue va = results.next(); String out = "2^3 = " + va.asNumber(); System.out.println(out); } // If the resolver reuses ExternalFunction objects, it is // responsible for eventually calling delete() resolver.close(); } catch (XmlException xe) { String out = "XmlException: " + xe.getMessage() + "\n"; System.out.println(out); }