Premature end of primary result serialization
Recent refactorisation of the transformer->destination interface, pushing the responsibility for receiver closure onto
Destination.closeAndNotify() appears in at least one instance, not to be flushing the
The circumstances appear difficult to minimise (it's the XSLT-based compiler running, carrying out a normal XML serialization to primary output, with no additional result documents), but in this case in
XSLT30Transformer.applyTemplates()#305 at the invocation of
Receiver sOut (which is actually a
CloseNotifier) has a trail:
RegularSequenceChecker -> SequenceNormalizerWithSpaceSeparator -> XMLIndenterPE -> XMLEmitter
UTF-8Writer as the
writer field of the
XMLEmitter and for which the buffer hasn't been flushed to the file output stream.
destination is a
helper field is a
DestinationHelper with no
listeners. Consequently on
closeAndNotify() the output stream is closed and no action is taken on flushing or closing the
UTF8Writer, which in any case would have to happen before the stream was closed.
As a temporary measure restoring a
sOut.close() action before
destination.closeAndNotify() fixes the issue, but I think something elsewhere might be better.
#1 Updated by Michael Kay about 1 year ago
From the description, it sounds as if the serializer is being initialized with a Writer rather than an OutputStream.
The Javadoc for Serializer.setOutputWriter() says "Closing the writer after use is the responsibility of the caller.".
and this seems to be true: Serializer.mustClose is set to false, and Serializer.close() therefore does not close the writer. The
mustClose flag is set only if either (a) a File was supplied as the destination, or (b) there has been an explicit call on
This design follows the general principle that the "person" who creates a stream, reader, or writer is responsible for closing it after use.
With secondary result documents the situation is more complicated, because the OutputURIResolver (or equivalent) only gets a chance to close the stream after use if we call it to do so after writing the output. That's why we allow the creator of the Serializer to request auto-close on completion.
So the question is, where is the writer coming from in your case?
#2 Updated by John Lumley about 1 year ago
Problem still persists after recent (31st July) updates.... The receiver sOut trail has been reduced to SequenceNormalizerWithSpaceSeparator -> XMLIndenterPE -> XMLEmitter, but otherwise pretty much the same. Closing sOut before destination.closeAnd Notify() corrects the issue.
The destination is a Serializer with a StreamResult .result field (for which .writer is nul)l and .mustClose is true (The output is indeed going to be a file as it is generated from the -o: argument of net.sf.saxon.Transform.main())
I don't appear to be doing anything at all out of the ordinary - merely executing net.sf.saxon.Transform.main() programmatically (in IntelliJ) rather than from the command line
#4 Updated by Michael Kay about 1 year ago
It's working for me, simply running Transform from the command line with -o:out.xml.
The Serializer is initialized using setOutputFile(File), which sets mustClose=true,
The Xslt30Transformer has the sequence:
Receiver sOut = getDestinationReceiver(controller, destination); applyTemplatesToSource(source, sOut); destination.closeAndNotify();
applyTemplatesToSource() invokes controller.applyTemplates(), which on completion does out.close(). out is a ComplexContentOutputter() and the close() trickles down the pipeline to reach XMLEmitter.close(), which flushes the writer and (by calling super.close()) closes the FileOutputStream - though not the writer.
After controller.applyTemplates(), the xslt30Transformer invokes destination.closeAndNotify(), which invokes helper.closeAndNotify(); the helper is a DestinationHelper with helpee being the Serializer; so it calls helpee.close(), and Serializer.close() finds mustClose set and this triggers stream.close(); but this is academic because it has already been closed.
I can't see that the failure to close the writer is significant if we've flushed the writer and closed the OutputStream: UTF8Writer.close() doesn't do anything of significance other than flush() and out.close().
There could be a problem, I think, if the close() call in XsltController.applyTemplates() isn't filtering all the way to the XMLEmitter. That would suggest some problem with the way the pipeline has been put together.
#5 Updated by John Lumley about 1 year ago
- Status changed from New to Rejected
My fault - in a previous situation I had to remove the close() action in XsltController.applyTempates() (can't recall the exact circumstances....) My commenting patch merged with the updates leading to non-closure of the writer.
Sorry for the alarm - at least I know a bit more about the receiver chains.....
Please register to edit this issue