Investigating into opportunities for an implementation of EXPath Packaging for eXist, I am looking at the way to implement extension functions with Java. I detail here the key points I found to write Java extensions for eXist using NetBeans. But those info should be generic enough to be used with any Java IDE.
If you want to provide an extension function written in Java within eXist, you actually have to provide a full module in Java. You cannot provide the module part in XQuery and part in Java (though you can provide an XQuery module that imports a private module written in Java.)
The module is represented by a class that extends the eXist abstract class AbstractInternalModule. Along some information like the namespace URI of the module, the default prefix to use, and a description, you have to provide a list of extension functions through the base class constructor:
public class SimpleModule extends AbstractInternalModule { public SimpleModule() { super(FUNCTIONS); } public String getNamespaceURI() { return NAMESPACE_URI; } public String getDefaultPrefix() { return PREFIX; } public String getDescription() { return "A simple example module"; } static final String PREFIX = "ext"; static final String NAMESPACE_URI = "http://www.example.org/project/ext"; private final static FunctionDef[] FUNCTIONS = { new FunctionDef(SimpleFunction.SIGNATURE, SimpleFunction.class) }; }
This module contains one single function, implemented by the class
SimpleFunction
. An extension function is a Java class that extends the eXist abstract
class BasicFunction. It has to provide some information to the base class constructor
(its qualified name, a documentation string, its parameter list and its return type.)
The
logic of the function itself is located in the eval function:
// implements the function "ext:hello($who as xs:string) as element()". public class SimpleFunction extends BasicFunction { public SimpleFunction(XQueryContext ctxt) { super(ctxt, SIGNATURE); } /** return an element <hello> with the value of the string param as content */ public Sequence eval(Sequence[] args, Sequence unused) throws XPathException { String type = Integer.toString(args[0].getItemType()); MemTreeBuilder builder = context.getDocumentBuilder(); builder.startDocument(); builder.startElement(new QName("hello", null, null), null); builder.characters(args[0].getStringValue()); builder.endElement(); builder.endDocument(); return (Sequence) builder.getDocument().getDocumentElement(); } private static final FunctionParameterSequenceType PARAM_WHO = new FunctionParameterSequenceType("who", Type.STRING, Cardinality.EXACTLY_ONE, "Who?"); private static final FunctionReturnSequenceType RETURN_TYPE = new FunctionReturnSequenceType(Type.ELEMENT, Cardinality.EXACTLY_ONE, "Hello element"); static final FunctionSignature SIGNATURE = new FunctionSignature( new QName("hello", SimpleModule.NAMESPACE_URI, SimpleModule.PREFIX), "Return greetings." + " This method returns an element 'hello' with the name passed as param.", new SequenceType[] { PARAM_WHO }, RETURN_TYPE ); }
In order to compile those files with NetBeans, you have to add two JAR files to the
build
path of the project you used for those files. Right-click on the project and choose
Properties
, then Libraries
and the button Add JAR/Folder
. You
will find the JARs in the eXist install directory: $EXIST_HOME/exist.jar
and
$EXIST_HOME/lib/core/xmldb.jar
. Then you compile your project to get the JAR file
with your both class (the module and its single function) compiled. The best choice
for the
project's type is a library of Java classes.
Now you have the JAR file containing the Java implementation of your module and its
function. The next step is to plug them within an eXist instance. Copy the JAR file
to
$EXIST_HOME/lib/extensions/
and configure it in $EXIST_HOME/conf.xml
. The
configuration only requires to add a new module element to the builtin-modules
element:
<module class="org.example.project.SimpleModule" uri="http://www.example.org/project/ext"/>
Just restart your eXist instance, and try the following query in the admin console:
import module namespace ext="http://www.example.org/project/ext"; ext:hello('you')
This will call the method eval on a SimpleFunction
object, discovered through a
SimpleModule
object configured in conf.xml
. Soon, you should be able to
package the JAR file to an EXPath package and install it within eXist through a graphical
console...
Posted by Florent Georges, on 2009-07-31T23:55:00, tags: exist, expath and xquery.