This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new 29b1955609 Fix #4472: Support XSLT includes in native mode
29b1955609 is described below
commit 29b1955609ab356e6652440b6bb527b331b5ccb8
Author: Ramu <[email protected]>
AuthorDate: Tue Aug 18 20:17:32 2026 +0530
Fix #4472: Support XSLT includes in native mode
---
.../ROOT/pages/reference/extensions/xslt.adoc | 4 +-
extensions/xslt/deployment/pom.xml | 6 +++
.../xslt/deployment/BuildTimeUriResolver.java | 47 ++++++++++++++++-
.../component/xslt/deployment/XsltProcessor.java | 1 +
.../xslt/deployment/BuildTimeUriResolverTest.java | 59 ++++++++++++++++++++++
.../deployment/src/test/resources/xslt/leaf.xsl | 24 +++++++++
.../src/test/resources/xslt/nested-include.xsl | 22 ++++++++
.../src/test/resources/xslt/nested/mid.xsl | 22 ++++++++
.../src/test/resources/xslt/nested/parent.xsl | 22 ++++++++
.../xslt/runtime/src/main/doc/configuration.adoc | 4 +-
.../src/main/resources/application.properties | 2 +-
.../camel/quarkus/component/xml/it/XsltTest.java | 1 -
12 files changed, 208 insertions(+), 6 deletions(-)
diff --git a/docs/modules/ROOT/pages/reference/extensions/xslt.adoc
b/docs/modules/ROOT/pages/reference/extensions/xslt.adoc
index 68aac03528..a49659fe0e 100644
--- a/docs/modules/ROOT/pages/reference/extensions/xslt.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/xslt.adoc
@@ -61,7 +61,9 @@ Scheme-less URIs are interpreted as `classpath:` URIs.
Only `classpath:` URIs are supported on Quarkus native mode. `file:`, `http:`
and other kinds of URIs can be used on JVM mode only.
-`<xsl:include>` and `<xsl:messaging>` XSLT elements are also supported in JVM
mode only right now.
+`<xsl:include>` is supported in native mode when the including stylesheet is
listed in `quarkus.camel.xslt.sources`
+and included stylesheets are classpath-relative. Nested classpath includes are
resolved at build time.
+`<xsl:messaging>` XSLT elements are supported in JVM mode only right now.
If `aggregate` DSL is used, `XsltSaxonAggregationStrategy` has to be used such
as
[source,java]
diff --git a/extensions/xslt/deployment/pom.xml
b/extensions/xslt/deployment/pom.xml
index 2ed87447b3..18b7edaf0a 100644
--- a/extensions/xslt/deployment/pom.xml
+++ b/extensions/xslt/deployment/pom.xml
@@ -48,6 +48,12 @@
<artifactId>nativeimage</artifactId>
<scope>provided</scope>
</dependency>
+
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit-internal</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/BuildTimeUriResolver.java
b/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/BuildTimeUriResolver.java
index 42de1b9bdf..827a5b5ec8 100644
---
a/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/BuildTimeUriResolver.java
+++
b/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/BuildTimeUriResolver.java
@@ -20,13 +20,19 @@ import java.io.IOException;
import java.net.URL;
import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;
import org.apache.camel.support.ResourceHelper;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.StringHelper;
-public class BuildTimeUriResolver {
+/**
+ * Resolves classpath XSLT sources at build time, including nested {@code
<xsl:include>}
+ * hrefs, so they can be compiled into Xalan translets.
+ */
+public class BuildTimeUriResolver implements URIResolver {
public ResolutionResult resolve(String templateUri) {
if (templateUri == null) {
@@ -49,7 +55,8 @@ public class BuildTimeUriResolver {
throw new IllegalStateException("Could not find the XSLT resource
" + compacted + " in the classpath");
}
try {
- final Source src = new StreamSource(url.openStream());
+ final String systemId = effectiveScheme + compacted;
+ final Source src = new StreamSource(url.openStream(), systemId);
// TODO: if the XSLT file is under target/classes of the current
project we should mark it for exclusion
// from the application archive. See
https://github.com/apache/camel-quarkus/issues/438
return new ResolutionResult(templateUri, transletName, src);
@@ -59,6 +66,42 @@ public class BuildTimeUriResolver {
}
+ @Override
+ public Source resolve(String href, String base) throws
TransformerException {
+ try {
+ return resolve(toClasspathUri(href, base)).source;
+ } catch (RuntimeException e) {
+ throw new TransformerException(
+ "Could not resolve XSLT include href '" + href + "' with
base '" + base + "'", e);
+ }
+ }
+
+ static String toClasspathUri(String href, String base) {
+ if (href == null) {
+ throw new RuntimeException("href parameter cannot be null");
+ }
+ href = href.trim();
+ if (href.isEmpty()) {
+ throw new RuntimeException("href parameter cannot be empty");
+ }
+
+ final String hrefScheme = ResourceHelper.getScheme(href);
+ if (hrefScheme != null) {
+ return href;
+ }
+
+ if (base == null || base.isBlank()) {
+ return href;
+ }
+
+ final String baseScheme = ResourceHelper.getScheme(base);
+ final String basePath = baseScheme != null ? StringHelper.after(base,
baseScheme) : base;
+ final int slash = basePath.lastIndexOf('/');
+ final String dir = slash >= 0 ? basePath.substring(0, slash + 1) : "";
+ final String scheme = baseScheme != null ? baseScheme : "classpath:";
+ return scheme + dir + href;
+ }
+
private String toTransletName(final String compacted) {
final String fileName = FileUtil.stripPath(compacted);
final String name = FileUtil.stripExt(fileName, true);
diff --git
a/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/XsltProcessor.java
b/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/XsltProcessor.java
index 165af2a767..950279dc73 100644
---
a/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/XsltProcessor.java
+++
b/extensions/xslt/deployment/src/main/java/org/apache/camel/quarkus/component/xslt/deployment/XsltProcessor.java
@@ -113,6 +113,7 @@ class XsltProcessor {
tf.setAttribute("package-name", config.packageName());
tf.setAttribute("destination-directory",
destination.toString());
tf.setErrorListener(new CamelXsltErrorListener());
+ tf.setURIResolver(resolver);
tf.newTemplates(resolvedUri.source);
} catch (TransformerException e) {
throw new RuntimeException("Could not compile XSLT " +
uri, e);
diff --git
a/extensions/xslt/deployment/src/test/java/org/apache/camel/quarkus/component/xslt/deployment/BuildTimeUriResolverTest.java
b/extensions/xslt/deployment/src/test/java/org/apache/camel/quarkus/component/xslt/deployment/BuildTimeUriResolverTest.java
new file mode 100644
index 0000000000..7e8032955b
--- /dev/null
+++
b/extensions/xslt/deployment/src/test/java/org/apache/camel/quarkus/component/xslt/deployment/BuildTimeUriResolverTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.xslt.deployment;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerFactory;
+
+import org.apache.camel.quarkus.support.xalan.XalanTransformerFactory;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class BuildTimeUriResolverTest {
+
+ @Test
+ void systemIdIsClasspathUri() {
+ BuildTimeUriResolver resolver = new BuildTimeUriResolver();
+ BuildTimeUriResolver.ResolutionResult resolved =
resolver.resolve("xslt/nested-include.xsl");
+
+ assertEquals("classpath:xslt/nested-include.xsl",
resolved.source.getSystemId());
+ }
+
+ @Test
+ void relativeIncludeIsResolvedAgainstParent() throws Exception {
+ BuildTimeUriResolver resolver = new BuildTimeUriResolver();
+ Source included = resolver.resolve("mid.xsl",
"classpath:xslt/nested/parent.xsl");
+
+ assertEquals("classpath:xslt/nested/mid.xsl", included.getSystemId());
+ }
+
+ @Test
+ void nestedClasspathIncludesAreCompiled() throws Exception {
+ BuildTimeUriResolver resolver = new BuildTimeUriResolver();
+ BuildTimeUriResolver.ResolutionResult resolved =
resolver.resolve("xslt/nested-include.xsl");
+
+ TransformerFactory tf = new XalanTransformerFactory();
+ tf.setURIResolver(resolver);
+ Templates templates = tf.newTemplates(resolved.source);
+
+ assertNotNull(templates);
+ }
+
+}
diff --git a/extensions/xslt/deployment/src/test/resources/xslt/leaf.xsl
b/extensions/xslt/deployment/src/test/resources/xslt/leaf.xsl
new file mode 100644
index 0000000000..70f1ce7f3f
--- /dev/null
+++ b/extensions/xslt/deployment/src/test/resources/xslt/leaf.xsl
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:template match="/">
+ <ok/>
+ </xsl:template>
+</xsl:stylesheet>
diff --git
a/extensions/xslt/deployment/src/test/resources/xslt/nested-include.xsl
b/extensions/xslt/deployment/src/test/resources/xslt/nested-include.xsl
new file mode 100644
index 0000000000..acc27ed2fd
--- /dev/null
+++ b/extensions/xslt/deployment/src/test/resources/xslt/nested-include.xsl
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:include href="nested/parent.xsl"/>
+</xsl:stylesheet>
diff --git a/extensions/xslt/deployment/src/test/resources/xslt/nested/mid.xsl
b/extensions/xslt/deployment/src/test/resources/xslt/nested/mid.xsl
new file mode 100644
index 0000000000..0fa66d5955
--- /dev/null
+++ b/extensions/xslt/deployment/src/test/resources/xslt/nested/mid.xsl
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:include href="../leaf.xsl"/>
+</xsl:stylesheet>
diff --git
a/extensions/xslt/deployment/src/test/resources/xslt/nested/parent.xsl
b/extensions/xslt/deployment/src/test/resources/xslt/nested/parent.xsl
new file mode 100644
index 0000000000..f8d1d0a1f2
--- /dev/null
+++ b/extensions/xslt/deployment/src/test/resources/xslt/nested/parent.xsl
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:include href="mid.xsl"/>
+</xsl:stylesheet>
diff --git a/extensions/xslt/runtime/src/main/doc/configuration.adoc
b/extensions/xslt/runtime/src/main/doc/configuration.adoc
index 85262d3b42..d3ed2a04b4 100644
--- a/extensions/xslt/runtime/src/main/doc/configuration.adoc
+++ b/extensions/xslt/runtime/src/main/doc/configuration.adoc
@@ -11,7 +11,9 @@ Scheme-less URIs are interpreted as `classpath:` URIs.
Only `classpath:` URIs are supported on Quarkus native mode. `file:`, `http:`
and other kinds of URIs can be used on JVM mode only.
-`<xsl:include>` and `<xsl:messaging>` XSLT elements are also supported in JVM
mode only right now.
+`<xsl:include>` is supported in native mode when the including stylesheet is
listed in `quarkus.camel.xslt.sources`
+and included stylesheets are classpath-relative. Nested classpath includes are
resolved at build time.
+`<xsl:messaging>` XSLT elements are supported in JVM mode only right now.
If `aggregate` DSL is used, `XsltSaxonAggregationStrategy` has to be used such
as
[source,java]
diff --git
a/integration-test-groups/xml/native/xslt-classpath/src/main/resources/application.properties
b/integration-test-groups/xml/native/xslt-classpath/src/main/resources/application.properties
index e3c54d8308..3473652d9c 100644
---
a/integration-test-groups/xml/native/xslt-classpath/src/main/resources/application.properties
+++
b/integration-test-groups/xml/native/xslt-classpath/src/main/resources/application.properties
@@ -23,5 +23,5 @@
quarkus.log.category."org.apache.camel.quarkus.core.deployment".level = INFO
#
# Quarkus - Camel
#
-quarkus.camel.xslt.sources =
xslt/classpath-transform.xsl,xslt/html-transform.xsl,xslt/html-to-text.xsl,xslt/extension-function.xsl,xslt/include_not_existing_resource.xsl,xslt/aggregate.xsl,xslt/terminate.xsl
+quarkus.camel.xslt.sources =
xslt/classpath-transform.xsl,xslt/html-transform.xsl,xslt/html-to-text.xsl,xslt/extension-function.xsl,xslt/include.xsl,xslt/include_not_existing_resource.xsl,xslt/aggregate.xsl,xslt/terminate.xsl
quarkus.camel.xslt.features."http\://javax.xml.XMLConstants/feature/secure-processing"
= false
diff --git
a/integration-test-groups/xml/native/xslt-classpath/src/test/java/org/apache/camel/quarkus/component/xml/it/XsltTest.java
b/integration-test-groups/xml/native/xslt-classpath/src/test/java/org/apache/camel/quarkus/component/xml/it/XsltTest.java
index 231c90c779..8aa1de67dd 100644
---
a/integration-test-groups/xml/native/xslt-classpath/src/test/java/org/apache/camel/quarkus/component/xml/it/XsltTest.java
+++
b/integration-test-groups/xml/native/xslt-classpath/src/test/java/org/apache/camel/quarkus/component/xml/it/XsltTest.java
@@ -139,7 +139,6 @@ public class XsltTest {
@ParameterizedTest
@ValueSource(strings = { "xslt", "xslt-saxon" })
- @DisabledOnIntegrationTest("Generating xslt templates dynamically does not
be supported in native mode")
public void xsltInclude(String component) {
final String actual = RestAssured.given()
.body(BODY)