Bug #2236
closedParameters set using Transformer.setParameter() not accessible using getParameter()
100%
Description
I had been successfully using the Java extension functions: full interface (http://www.saxonica.com/documentation/html/extensibility/integratedfunctions/ext-full-J.html) throughout the 9.5 versions.
Specifically, I was able to access a custom "global context" of the running application, to be able to access cached items and other resources. Using the standard javax.xml.transform.Transformer
API obtained from a call to Templates.newTransformer
(which was actually returned as a net.sf.saxon.Controller@) as "@t@", I was able to call @t.setParameter("myGlobalContextKey", myGlobalContext)
before calling @t.transform(...)@. Any parameters set here could be accessed by the Transformer's XSLT - as well as from the XPathContext within an ExtensionFunctionCall, by calling:
xPathContext.getController().getParameter("myGlobalContextKey")
In 9.6, this is no longer working. The value returned by getParameter is now always null. In 9.5, Controller.setParameter
was pretty straight-forward. In 9.6, this is now implemented by net.sf.saxon.jaxp.TransformerImpl.setParameter
- which among other things, will immediately return without setting anything if the parameter was not found to exist on the xsltExecutable / defined as a parameter within the XSLT.
At least in my scenarios, the XSLTs are to remain ignorant of this functionality - they should not need to define parameters within themselves, just because they may be needed internally by the other custom extension functions that they are able to call.
I have referenced the following change documentation - both of which directly seem to related to this change:
-
http://www.saxonica.com/documentation/#!changes/JAXP/9.5.1-9.6
-
http://www.saxonica.com/documentation/#!changes/s9api/9.5.1-9.6
However, I don't see an equivalent replacement of the parameter handling.
I would like to avoid referencing the Controller if at all possible, as it is now documented as "From 9.6 this class should no longer be considered a public API.". Even worse, I would not like to access its .getBindery
- documented as "This method is intended for internal use only." I don't see any other way to pass arbitrary data between a javax.xml.transform.Transformer instance and a ExtensionFunctionCall that it will call. This leaves me with 2 remaining options that I can currently see:
-
Cast the
Transformer
to aTransformerImpl@, then call its @.getUnderlyingController().setUserData(...)
method. This is documented as "This method is intended primarily for internal use, though it may also be used by advanced applications." I could then retrieve the data from the ExtensionFunctionCall using @xPathContext.getController().getUserData(...)@. -
Bypass the Saxon API entirely, and use some combination of a ThreadLocal hack combined with a Map of the current Transformer along with the custom global context I'm hoping to pass.
Any guidance or recommendations would be much appreciated. Thanks!
Updated by Mark Ziesemer almost 10 years ago
My above #1 idea is not working, as clearPerTransformationData()
is called at the beginning of Controller.transform(Source source, Receiver receiver)
- clearing about any userData I had set in advance.
Are there any other suggestions I should consider, shy of resorting to something based on a ThreadLocal here?
Updated by Michael Kay almost 10 years ago
- Status changed from New to In Progress
- Assignee set to Michael Kay
The method Controller.getParameter() has been reinstated in version 9.6.0.2 as a fix to bug https://saxonica.plan.io/issues/2170 - It's slightly different from the original in its type signature, but it serves the original purpose.
Updated by Mark Ziesemer almost 10 years ago
Michael - thanks. I found the Controller.getParameter(), but it is not able to access the parameters I had set due to what I had mentioned above:
...TransformerImpl.setParameter - which among other things, will immediately return without setting anything if the parameter was not found to exist on the xsltExecutable / defined as a parameter within the XSLT. ...
Maybe deserving for a new ticket under "JAXP Java API", from https://docs.oracle.com/javase/7/docs/api/javax/xml/transform/Transformer.html#getParameter%28java.lang.String%29 :
public abstract Object getParameter(String name)
Get a parameter that was explicitly set with setParameter.
However, as of Saxon 9.6, this is no longer the case. Consider the following test code:
public void test() throws Exception{
// Java 1.8.0_25 default:
test(transformerFromXslt(TransformerFactory.newInstance(
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", getClass().getClassLoader())));
// Apache Xalan-2.7.1:
test(transformerFromXslt(TransformerFactory.newInstance(
"org.apache.xalan.processor.TransformerFactoryImpl", getClass().getClassLoader())));
// Saxon-HE-9.6.0-2:
test(transformerFromXslt(new net.sf.saxon.TransformerFactoryImpl()));
}
protected Transformer transformerFromXslt(final TransformerFactory tf) throws Exception{
final StreamSource ss = new StreamSource(getClass().getResourceAsStream("Identity.xslt"));
final Templates templates = tf.newTemplates(ss);
final Transformer transformer = templates.newTransformer();
return transformer;
}
protected void test(final Transformer t){
System.out.println("t.getClass: " + t.getClass());
t.setParameter("testParam", "good");
System.out.println("t.getParameter: " + t.getParameter("testParam"));
}
The following output is generated:
t.getClass: class com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl
t.getParameter: good
t.getClass: class org.apache.xalan.transformer.TransformerImpl
t.getParameter: good
t.getClass: class net.sf.saxon.jaxp.TransformerImpl
t.getParameter: null
It appears that Saxon is no longer compliant here with the JAXP API.
Updated by Michael Kay almost 10 years ago
- Tracker changed from Support to Bug
- Subject changed from How to access parameters set using Transformer.setParameter from XPathContext in ExtensionFunctionCall in 9.6? to Parameters set using Transformer.setParameter() not accessible using getParameter()
OK, it had never occurred to me that someone might use setParameter() to set something that isn't defined as a parameter, and expect it to do anything useful. But it's easy enough to fix, so I'll fix it. (Note, I don't guarantee to fix every incompatibility with JAXP. The JAXP spec is too vaguely worded to make that possible, there are too many variations permitted to achieve 100% compatibility, and most importantly, it has never been updated to support XSLT 2.0 let alone XSLT 3.0. Also, it's not particularly stable - although the formal method signatures stay the same, there have been many "clarifications" over the years to the prose saying what the methods are supposed to do, sometimes simply to document what I would regard as bugs in the Xalan implementation.)
So I am changing Transformer.setParameter() and getParameter() so that getParameter() always returns what setParameter() has set, even if it is not defined in the stylesheet. However, I'm not sure how useful this is to the original use case, since the Transformer is not accessible to extension functions via the XPathContext object. Only the Controller is accessible, and the Controller will only know about those parameters that are relevant to the stylesheet. Of course, you could use Controller.setUserData() to provide a route to the Transformer if you want.
Updated by Michael Kay almost 10 years ago
- Category changed from Saxon extensions to JAXP Java API
- Status changed from In Progress to Resolved
- Priority changed from Low to Normal
- Found in version set to 9.6
Updated by Mark Ziesemer almost 10 years ago
Agreed on all of your points - and thank you for fixing the easy fix!
Would you please further consider ensuring that all set parameters can be accessed from the XPathContext object - either through the Controller, or possibly providing an accessor to the current Transformer, e.g. XPathContext.getTransformer()
(with the understanding it would be null for non-XSLT contexts, similar to @XPathContext.getController()@)?
Alternatively, any parameters that are set "today" (in 9.6.0-2) are available through the alternate syntax of Controller.getParameter()
- as you mentioned above. The only problem was that parameters that were set that weren't defined were not being persisted into the bindery. If it would be possible to allow such parameters to pass-through as a "Java" type, I think this would provide for both concerns - allowing such parameters to be returned by both Transformer.getParameter()
as well as @Controller.getParameter()@.
I will be more than willing to file an additional enhancement request for one or both of these suggestions, if you would be in agreement.
Thanks again!
Updated by O'Neil Delpratt almost 10 years ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in version set to 9.6.0.3
Bug fix patch applied to the Saxon 9.6.0.3 maintenance release
Updated by O'Neil Delpratt almost 9 years ago
- Sprint/Milestone set to 9.6.0.3
- Applies to branch 9.6 added
- Fix Committed on Branch 9.6 added
- Fixed in Maintenance Release 9.6.0.3 added
Please register to edit this issue