Project

Profile

Help

Bug #4019

closed

Problems with reuse of Transformer instances

Added by Anonymous about 6 years ago. Updated almost 6 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
JAXP Java API
Sprint/Milestone:
-
Start date:
2018-11-12
Due date:
% Done:

100%

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

Description

Starting with SaxonHE version 9.9.0-1 there is a problem with reuse of Transformer instances. If you have variables set in your XSLT outside of a template they are not cleared when you reuse the Transformer with another XML input.

The attached code example illustrates the issue. In the XSLT there is a variable populated with a value selected from the XML. The same Transformer instance is then used for two transformations in a row with different XML input. Both times the same value is output.


Files

saxon-test.zip (60.7 KB) saxon-test.zip Anonymous, 2018-11-12 01:15
Actions #1

Updated by Michael Kay about 6 years ago

  • Category set to JAXP Java API
  • Assignee set to Michael Kay
  • Priority changed from High to Normal

Thanks for reporting this.

As a workaround, please create a new Transformer for each transformation.

My recommendation is to compile the stylesheet once to a Templates object (using factory.newTemplates()) and then create a new Transformer for each transformation using Templates.newTransformer(). The JAXP specification says that what you are doing should work, so this is indeed a bug, but with Saxon it is generally cleaner to let the garbage collector dispose of the resources held by the Transformer object once it's finished.

Actions #2

Updated by Michael Kay about 6 years ago

Problem reproduced with JUnit test jaxptest/TransformTest/testTransformerReuse

Actions #3

Updated by Michael Kay about 6 years ago

  • Status changed from New to In Progress

Getting this right is trickier than it might appear, because the JAXP Transformer is layered on top of the s9api XsltTransformer, which in turn is layered on top of the internal XsltController; in effect we need to work out how serial reuse should behave at each of these three levels. We should also get it right for the Xslt30Transformer while we're about it. (One option is that the JAXP Transformer should create a new s9api XsltTransformer for each transformation, which might simplify things a little, but it doesn't solve the whole problem).

The problem is that calling transform() has side-effects on the state of the XsltTransformer (and the underying XsltController) which it's hard to undo afterwards. For example, it causes XsltTransformer.initialSource to be set, exactly as if the user had called setSource() directly, and we cannot afterwards tell whether the setting was due to an explicit or implicit call. Which raises another complication, namely that if setSource() supplies a DOMSource, then it is reasonable to use the same DOMSource again for subsequent transformations, but if a SAXSource or StreamSource is supplied, then it is impossible to use it again, because it is consumed by use.

The Javadoc for XsltTransformer says that serial reuse is allowed, and that running the tranformation "does not change the context that has been established". This is clearly an over-simplification. For example, it also says (under getBaseOutputURI()) "If no value has been set explicitly, then the method returns null if called before the transformation, or the computed default base output URI if called after the transformation."

Actions #4

Updated by Michael Kay about 6 years ago

  • Fixed in Maintenance Release 9.7.0.10 added

As a start, I have been documenting more carefully in XsltTransformer the effect on the state of the XsltTransformer of calling the transform() method; in particular the effect on the Source and Destination (both of which, in the general case, can only be used once).

I think it's best if transform() doesn't modify the value of the initialSource variable, so I have changed this. This means that, for example, if initialSource is supplied as a StreamSource, it will still be the same StreamSource object after the transformation, rather than (as now) being the document node of the tree built from that StreamSource. (This means that currently, an application might be able to supply a StreamSource and then transform it twice; after this change this will no longer be possible. But it only works currently by accident, and is undocumented).

The next question is the state of the XsltController after a call on callTemplate() or applyTemplatesToSource() or applyStreamingTemplates(); also, XsltController.transform() explicitly calls XsltController.setGlobalContextItem() (which is the immediate cause of the trouble in this bug).

Because the XsltController carries such a lot of state, and it's hard to ensure that this is unaffected by a transformation, it's tempting to suggest that the s9api XsltTransformer and Xslt30Transformer should only create an XsltController on demand, creating a new one for each transformation. At first sight this looks like a rather extensive change, but I shall experiment with it.

Actions #5

Updated by Michael Kay about 6 years ago

On reflection, I think there are probably many applications that rely on calling XsltTransformer.getUnderlyingController() to achieve advanced effects, so changing the XsltTransformer to create the Controller dynamically would probably be too disruptive a change.

Actions #6

Updated by Michael Kay about 6 years ago

  • Status changed from In Progress to Resolved
  • Fix Committed on Branch 9.9 added

I have solved this particular test case by tweaking XsltTransformer.transform() so that if the global context item in the XsltController is null on entry to the method, then it is reset to null on completion of the method (whether the transformation succeeds or fails).

With the Xslt30Transformer we don't have the same complications because we disallow serial reuse.

Fixed as described on 9.9 branch. I have checked that the problem does not exist on 9.8.

Actions #7

Updated by O'Neil Delpratt almost 6 years ago

  • % Done changed from 0 to 100
  • Fixed in Maintenance Release 9.9.1.1 added
  • Fixed in Maintenance Release deleted (9.7.0.10)

Bug fix applied to the Saxon 9.9.1.1 maintenance release.

Actions #8

Updated by O'Neil Delpratt almost 6 years ago

  • Status changed from Resolved to Closed

Please register to edit this issue

Also available in: Atom PDF