Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xslt:message in Saxon 9.4 vs Saxon 9.6

Tags:

java

xml

xslt

saxon

I'm trying to throw exception from java code that will include the message from xsl:message tag when using Saxon.

Using the following xslt file

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:message terminate="yes">exception message</xsl:message>
    </xsl:template>
</xsl:stylesheet>

On Saxon 9.4 with the following code

public static void main(String[] args) throws TransformerException {
    try {
        TransformerFactory fact = new net.sf.saxon.TransformerFactoryImpl();
        Transformer newTransformer = fact.newTransformer(new StreamSource(new File("throw.xslt")));
        ((net.sf.saxon.Controller)newTransformer).setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
        ((net.sf.saxon.Controller)newTransformer).setMessageEmitter(new MessageWarner());
        newTransformer.transform(new StreamSource(new File("input.xml")), new StreamResult(new File("output.xml")));
    } catch (TransformerException e) {
        System.out.println("THIS IS EXCEPTION: " + e.getMessage() + " <<<");
        throw e;
    }
}   

it gives THIS IS EXCEPTION: exception message <<<, which is the behavior I'm expecting.

But on Saxon 9.6 with a little adjusted code because of API changes

public static void main(String[] args) throws TransformerException {
    try {
        TransformerFactory fact = new net.sf.saxon.TransformerFactoryImpl();
        Transformer newTransformer = fact.newTransformer(new StreamSource(new File("throw.xslt")));
        ((net.sf.saxon.jaxp.TransformerImpl)newTransformer).getUnderlyingController().setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
        ((net.sf.saxon.jaxp.TransformerImpl)newTransformer).getUnderlyingController().setMessageEmitter(new MessageWarner());
        newTransformer.transform(new StreamSource(new File("input.xml")), new StreamResult(new File("output.xml")));
    } catch (TransformerException e) {
        System.out.println("THIS IS EXCEPTION: " + e.getMessage() + " <<<");
        throw e;
    }
}

it gives THIS IS EXCEPTION: Processing terminated by xsl:message at line 4 in throw.xslt <<< and the xsl:message is somewhere lost.

How can I achieve the "9.4" behavior on "9.6"?

like image 267
tomgi Avatar asked Oct 26 '15 15:10

tomgi


2 Answers

This is due to the message listener, to which MessageEmitter is sending messages. In saxon 9.6 the default listener is implementing UnfailingErrorListener which cannot throw exception (as all the other listeners in 9.6), but in 9.4 it was possible to throw exceptions from listeners.

However, you can implement your own message emitter, that would throw an exception while encountering xml:message with terminate set to yes, like this:

final class ExceptionThrowingMessageEmitter extends XMLEmitter {
  boolean abort = false;

  public void startDocument(int properties) throws XPathException {
    setWriter(new StringWriter());
    abort = (properties & ReceiverOptions.TERMINATE) != 0;
    super.startDocument(properties);
  }

  public void endDocument() throws XPathException {
    XPathException de = new XPathException(getWriter().toString());
    de.setErrorCode("XTMM9000");
    if (abort) {
      throw de;
    } else {
      //terminate set to no, do something like writing to the log file
    }
  }

  public void close() {
    // do nothing
  }
}

and then, register it like this:

transformer.getUnderlyingController().setMessageEmitter(new ExceptionThrowingMessageEmitter());  

This way, exception will be thrown, when there is terminating xml:message

like image 106
Mateusz Urban Avatar answered Sep 30 '22 19:09

Mateusz Urban


Unfortunately, the new Processing terminated... message is hardcoded in net.sf.saxon.expr.instruct.Message (Message.java:253 in 9.6.0-7). Here's a workaround that might give you what you need:

public static void main(String[] args) throws TransformerException {
    final StringWriter messageOut = new StringWriter();
    try {
        TransformerFactory fact = new net.sf.saxon.TransformerFactoryImpl();
        Transformer newTransformer = fact.newTransformer(new StreamSource(new File("throw.xslt")));
        ((net.sf.saxon.jaxp.TransformerImpl)newTransformer).getUnderlyingController().setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
        ((net.sf.saxon.jaxp.TransformerImpl)newTransformer).getUnderlyingController().setMessageEmitter(new MessageEmitter() {
                @Override
                public void open() throws XPathException {
                    setWriter(messageOut);
                    super.open();
                }
        });

        newTransformer.transform(new StreamSource(new File("input.xml")), new StreamResult(new File("output.xml")));
    } catch (TransformerException e) {
        System.out.println("THIS IS EXCEPTION: " + e.getMessage() + " <<<");
        String message = messageOut.toString(); // this is the "exception message\n" that you want
        // not sure why it has a \n on it
        System.out.println("THIS IS THE MESSAGE WE WANT: " + message);
        throw new TransformerException(message, e); // rethrow using the captured message, if you really want that "exception message" available to a caller in e.getMessage()
    }
}
like image 28
Alex Nauda Avatar answered Sep 30 '22 21:09

Alex Nauda