Project

Profile

Help

Bug #3856

closed

Premature end of primary result serialization

Added by John Lumley over 5 years ago. Updated over 5 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
Serialization
Sprint/Milestone:
-
Start date:
2018-07-23
Due date:
% Done:

0%

Estimated time:
Legacy ID:
Applies to branch:
trunk
Fix Committed on Branch:
Fixed in Maintenance Release:
Platforms:

Description

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 XMLEmitter.writer buffer.

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 destination.closeAndNotify(), the Receiver sOut (which is actually a CloseNotifier) has a trail:

RegularSequenceChecker -> SequenceNormalizerWithSpaceSeparator -> XMLIndenterPE -> XMLEmitter

with a 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 Serializer whose 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.

Actions #1

Updated by Michael Kay over 5 years 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 setCloseAfterUse().

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?

Actions #2

Updated by John Lumley over 5 years 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

Actions #3

Updated by Michael Kay over 5 years ago

Running Transform.main() from a Java application isn't really recommended practice - but if you must do it, take care to set -quit:off to ensure that the JVM isn't terminated after a failure.

Actions #4

Updated by Michael Kay over 5 years 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.

Actions #5

Updated by John Lumley over 5 years 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.....

Actions #6

Updated by John Lumley over 5 years ago

  • Status changed from Rejected to Closed

Closing

Please register to edit this issue

Also available in: Atom PDF