saxonbasexxslt-3.0jaxp

How to register Saxon/XSLT extension functions such that they accessible in BaseX/XQuery (JAXP APIs)


I have a few Saxon/XSLT extension functions that I created through the simple interface that I register like this:

Processor proc = new Processor(false);
proc.registerExtensionFunction(myExtensionFunctionInstance);

Now I want to use that with BaseX, but I see that BaseX instantiates its own Processor instance through JAXP APIs:

// Somewhere inside basex's XSLT transformer instantiation (see link above).
final TransformerFactory tf = TransformerFactory.newInstance();

So it seems I need to find a way to change the factory that BaseX uses such that I can register my functions.

If I get it right I need to create my own factory implementation. Adapted from Saxon's net.sf.saxon.TransformerFactoryImpl:

package my.package;
public class MyOwnTrasnformerFactory extends SaxonTransformerFactory {
    public TransformerFactoryImpl() {
        super();
        ExtensionFunction myFunction = new MyExtFunction(...);
        this.processor.registerExtensionFunction(myFunction);
    }

    public TransformerFactoryImpl(Configuration config) {
        super(config);
        ExtensionFunction myFunction = new MyExtFunction(...);
        this.processor.registerExtensionFunction(myFunction);
    }
}

And then I could do:

System.setProperty(
  "javax.xml.transform.TransformerFactory",
  "my.package.MyOwnTrasnformerFactory");

Solution

  • I ended up implementing the solution described on my question... here's a full gist:

    https://gist.github.com/EmmanuelOga/018ece8b4776636ce1ab3f73e215ce75

    The relevant parts:

    (ns rainbowfish.xslt-factory
      (:import [net.sf.saxon Configuration]
               [net.sf.saxon.lib Feature])
      (:gen-class
       :name rainbowfish.XsltFactory
       :extends net.sf.saxon.jaxp.SaxonTransformerFactory
       :post-init configure))
    
    (defn -configure
      "Generated Java class will call this method right after
      construction. Here we get a chance to grab Saxon's processor for
      configuration purposes (for example, adding extension functions)."
      [this & args]
      (let [processor (.getProcessor this)]
        ;; Here we can do whatever we want to the processor,
        ;; For instance, register some extension functions.
        (doto processor
          (.registerExtensionFunction some-function))))
    

    That defines the Java class rainbowfish.XsltFactory using clojure. Since it uses gen-class, it needs to be compiled by calling compile('rainbowfish.xslt-factory) or by asking lein to compile it, etc.

    Now that we have a class that configures the XSLT processor, we need to inform BaseX that it should use it. BaseX reads the JAXP configuration for this. The configuration can be set like this:

    (System/setProperty
         "javax.xml.transform.TransformerFactory",
         "rainbowfish.XsltFactory")
    

    ... this should be called before creating an instance of the BaseX server. See the gist for more details!