ppalaga commented on issue #4065:
URL: https://github.com/apache/camel-quarkus/issues/4065#issuecomment-2429377856
I wrote down this report for OpenJDK. Could you guys please review it before
we submit it?
---->8-----
Setting translet-name and package-name via TransformerFactory.setAttribute
has unexpected effects.
## Background
GraalVM native executables do not allow loading classes at runtime due to
its closed world assumption. To make XSLT work with them, we generate XSLT
Translet classes at build time and let native-image compile them into the
native executable. For that to work reliably, we need to be able to set the
name of the generated class so that we are then able to find the class and pass
it to the native compiler.
## Steps to reproduce
To generate a Translet class for a given XSL file, we perform steps similar
to the following:
```java
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
public class Main {
public static void main(String[] args) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute("generate-translet", true);
tf.setAttribute("translet-name", "MyTranslet");
tf.setAttribute("package-name", "org.acme");
tf.setAttribute("destination-directory", "test");
Path test = Path.of("test");
if (Files.exists(test)) {
Files.walk(test).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
}
File xslFile = new File(args[0]);
tf.newTemplates(new
StreamSource(Files.newInputStream(xslFile.toPath())));
Files.list(Path.of("test/org/acme")).forEach(System.out::println);
}
}
```
The reproducer code can be taken from
https://github.com/zhfeng/jdk-xalan-issue-reproducer .
When this program is compiled through `javac Main.java` and run via `java
Main test.xsl`, where `test.xsl` is any simple XSL file, such as
```xml
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8"/>
<xsl:template match="/request">
<xsl:variable name="name" select="//name/text()"/>
<response>
<message>
<xsl:value-of select="concat('Hello, ', $name)"/>
</message>
</response>
</xsl:template>
</xsl:stylesheet>
```
then, we *expect* to find the generated translet file under
`test/org/acme/MyTranslet.class`.
*In reality*, the generated translet is under
`test/org/acme/die_verwandlung.class`.
## Analysis
The execution flow goes via
`com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(Source)`,
where a new `com.sun.org.apache.xalan.internal.xsltc.trax.XSLTC` is created
and its `setClassName(String)` and `setPackageName(String)` are called.
`XSLTC.setClassName(String)` does some sanitization of the passed
`className` and if the `_packageName` field is set, it sets the `_className`
field to `_packageName + '.' + name`.
Because `XSLTC._packageName` is initialized to `"die.verwandlung"`, then,
after the first call of `setClassName("MyTranslet")`, the value of
`_packageName` is `"die.verwandlung.MyTranslet"`.
The `XSLTC.setPackageName("org.acme")` called afterwards, first sets the
`_packageName` field to the passed value and then, if `_className != null`, it
calls `setClassName(_className)`.
In our situation, it effectively means calling
`setClassName("die.verwandlung.MyTranslet")`.
The sanitization of the passed value done within this second
`setClassName()` call transforms `"die.verwandlung.MyTranslet"` into
`"die_verwandlung"`.
Afterwards, the `_className` field is set to `_packageName + '.' + name`
which is `"org.acme" + '.' + "die_verwandlung"` in our case.
Observation: the ASF Xalan does not initialize `XSLTC._packageName` to
`"die.verwandlung"` and therefore the reproducer code works as expected there.
## Possible solutions
A. In `TransformerFactoryImpl.newTemplates(Source)`, call
`XSLTC.setPackageName(String)` before `XSLTC.setClassName(String)`.
B. In `XSLTC.setPackageName(String)`, instead of calling
`setClassName(_className)`, pass only the simple class name extracted from the
`_className` field to `setClassName(String)`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]