Skip to content

Commit

Permalink
Fix apache#3904 to increase the xslt tests and support the schemas in…
Browse files Browse the repository at this point in the history
… JVM mode
  • Loading branch information
zhfeng committed Oct 24, 2022
1 parent 53b7036 commit b92f112
Show file tree
Hide file tree
Showing 18 changed files with 624 additions and 42 deletions.
22 changes: 16 additions & 6 deletions docs/modules/ROOT/pages/reference/extensions/xslt.adoc
Expand Up @@ -59,7 +59,22 @@ quarkus.camel.xslt.sources = transform.xsl, classpath:path/to/my/file.xsl

Scheme-less URIs are interpreted as `classpath:` URIs.

Only `classpath:` URIs are supported on Quarkus. `file:`, `http:` and other kinds of URIs do not work by design.
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` are aslo supported in JVM mode only right now.

If `aggregate` DSL is used, `XsltSaxonAggregationStrategy` has to be used such as
[source,java]
----
from("file:src/test/resources?noop=true&sortBy=file:name&antInclude=*.xml")
.routeId("aggregate").noAutoStartup()
.aggregate(new XsltSaxonAggregationStrategy("xslt/aggregate.xsl"))
.constant(true)
.completionFromBatchConsumer()
.log("after aggregate body: ${body}")
.to("mock:transformed");
----
Also, it's only supported on JVM mode.

[id="extensions-xslt-configuration-configuration"]
=== Configuration
Expand Down Expand Up @@ -89,11 +104,6 @@ The content of the XSLT source URIs is parsed and compiled into Java classes at
only source of XSLT information at runtime. The XSLT source files may not be included in the application archive at all.
====

[WARNING]
====
the extension does not yet support Java 11.
====


[width="100%",cols="80,5,15",options="header"]
|===
Expand Down
22 changes: 16 additions & 6 deletions extensions/xslt/runtime/src/main/doc/configuration.adoc
Expand Up @@ -9,7 +9,22 @@ quarkus.camel.xslt.sources = transform.xsl, classpath:path/to/my/file.xsl

Scheme-less URIs are interpreted as `classpath:` URIs.

Only `classpath:` URIs are supported on Quarkus. `file:`, `http:` and other kinds of URIs do not work by design.
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.

If `aggregate` DSL is used, `XsltSaxonAggregationStrategy` has to be used such as
[source,java]
----
from("file:src/test/resources?noop=true&sortBy=file:name&antInclude=*.xml")
.routeId("aggregate").noAutoStartup()
.aggregate(new XsltSaxonAggregationStrategy("xslt/aggregate.xsl"))
.constant(true)
.completionFromBatchConsumer()
.log("after aggregate body: ${body}")
.to("mock:transformed");
----
Also, it's only supported on JVM mode.
=== Configuration
TransformerFactory features can be configured using following property:
Expand All @@ -35,9 +50,4 @@ public class FunctionsConfiguration {
====
The content of the XSLT source URIs is parsed and compiled into Java classes at build time. These Java classes are the
only source of XSLT information at runtime. The XSLT source files may not be included in the application archive at all.
====

[WARNING]
====
the extension does not yet support Java 11.
====
Expand Up @@ -20,9 +20,12 @@

import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;

import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.annotations.Recorder;
import org.apache.camel.CamelContext;
import org.apache.camel.component.xslt.DefaultXsltUriResolverFactory;
import org.apache.camel.component.xslt.TransformerFactoryConfigurationStrategy;
import org.apache.camel.component.xslt.XsltComponent;
import org.apache.camel.component.xslt.XsltEndpoint;
Expand All @@ -37,7 +40,7 @@ public RuntimeValue<XsltComponent> createXsltComponent(CamelXsltConfig config,
final QuarkusTransformerFactoryConfigurationStrategy strategy = new QuarkusTransformerFactoryConfigurationStrategy(
config.packageName, config.features, uriResolver);
final XsltComponent component = new XsltComponent();
component.setUriResolver(uriResolver);
component.setUriResolverFactory(new QuarkusXsltUriResolverFactory(uriResolver));
component.setTransformerFactoryConfigurationStrategy(strategy);
component.setTransformerFactoryClass(XalanTransformerFactory.class.getName());
return new RuntimeValue<>(component);
Expand All @@ -52,6 +55,27 @@ public void addRuntimeUriResolverEntry(RuntimeValue<RuntimeUriResolver.Builder>
builder.getValue().entry(templateUri, transletClassName);
}

static class QuarkusXsltUriResolverFactory extends DefaultXsltUriResolverFactory {
private final RuntimeUriResolver uriResolver;

public QuarkusXsltUriResolverFactory(RuntimeUriResolver uriResolver) {
this.uriResolver = uriResolver;
}

@Override
public URIResolver createUriResolver(CamelContext camelContext, String resourceUri) {
// It is supposed to catch cases where we compile the translet at build time which is for classpath: XSLT
// resources in both JVM and native mode.
// Otherwise, all other cases will be handled by the default XsltUriResolver which is able to load a resource
// at runtime. So it is only supported in JVM mode.
if (uriResolver.getTransletClassName(resourceUri) != null) {
return uriResolver;
} else {
return super.createUriResolver(camelContext, resourceUri);
}
}
}

static class QuarkusTransformerFactoryConfigurationStrategy implements TransformerFactoryConfigurationStrategy {

private final String packageName;
Expand All @@ -68,23 +92,23 @@ public QuarkusTransformerFactoryConfigurationStrategy(String packageName, Map<St
@Override
public void configure(TransformerFactory tf, XsltEndpoint endpoint) {
final String className = uriResolver.getTransletClassName(endpoint.getResourceUri());

for (Map.Entry<String, Boolean> entry : features.entrySet()) {
try {
tf.setFeature(entry.getKey(), entry.getValue());
} catch (TransformerException e) {
throw new RuntimeException("Could not set TransformerFactory feature '"
+ entry.getKey() + "' = " + entry.getValue(), e);
if (className != null) {
for (Map.Entry<String, Boolean> entry : features.entrySet()) {
try {
tf.setFeature(entry.getKey(), entry.getValue());
} catch (TransformerException e) {
throw new RuntimeException("Could not set TransformerFactory feature '"
+ entry.getKey() + "' = " + entry.getValue(), e);
}
}
}

tf.setAttribute("use-classpath", true);
tf.setAttribute("translet-name", className);
tf.setAttribute("package-name", packageName);
tf.setErrorListener(new CamelXsltErrorListener());
tf.setAttribute("use-classpath", true);
tf.setAttribute("translet-name", className);
tf.setAttribute("package-name", packageName);
tf.setErrorListener(new CamelXsltErrorListener());

endpoint.setTransformerFactory(tf);
endpoint.setTransformerFactory(tf);
}
}

}
}
Expand Up @@ -57,14 +57,11 @@ public Source resolve(String href, String base) throws TransformerException {

/**
* @param uri the URI whose translet is seeked
* @return the unqualified translet name associated with the given {@code uri}
* @return the unqualified translet name associated with the given {@code uri} or
* {@code null} if the given XSLT resource was not compiled to a translet at build time.
*/
public String getTransletClassName(String uri) {
final String transletClassName = uriToTransletName.get(uri);
if (transletClassName == null) {
throw new RuntimeException("Could not resolve " + uri);
}
return transletClassName;
return uriToTransletName.get(uri);
}

/**
Expand Down
21 changes: 21 additions & 0 deletions integration-tests/xml/pom.xml
Expand Up @@ -35,6 +35,10 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-xslt</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-xslt-saxon</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-xpath</artifactId>
Expand All @@ -43,10 +47,22 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-stax</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-bean</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-direct</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-file</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-mock</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-seda</artifactId>
Expand Down Expand Up @@ -75,6 +91,11 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-integration-wiremock-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


Expand Down
Expand Up @@ -21,13 +21,20 @@
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import org.apache.camel.CamelContext;
import org.apache.camel.ConsumerTemplate;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.logging.Logger;

@Path("/xml")
Expand All @@ -42,11 +49,34 @@ public class XmlResource {
@Inject
ConsumerTemplate consumerTemplate;

@Inject
CamelContext camelContext;

@Path("/xslt")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String classpath(String body) {
return producerTemplate.requestBody("xslt:xslt/classpath-transform.xsl", body, String.class);
public String classpath(@QueryParam("output") @DefaultValue("string") String output, String body) {
if (output.equals("file")) {
return producerTemplate.requestBodyAndHeader("xslt:xslt/classpath-transform.xsl?output=file",
body, Exchange.XSLT_FILE_NAME, "target/xsltme.xml", String.class);
}
return producerTemplate.requestBody("xslt:xslt/classpath-transform.xsl?output=" + output, body, String.class);
}

@Path("/xslt_include")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String xsltInclude(String body) {
return producerTemplate.requestBody("xslt:xslt/include.xsl", body, String.class);
}

@Path("/xslt_terminate")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String xsltTerminate(String body) {
Exchange out = producerTemplate.request("xslt:xslt/terminate.xsl", exchange -> exchange.getIn().setBody(body));
Exception warning = out.getProperty(Exchange.XSLT_WARNING, Exception.class);
return warning.getMessage();
}

@Path("/xslt-extension-function")
Expand All @@ -56,6 +86,58 @@ public String extensionFunction(String body) {
return producerTemplate.requestBody("xslt:xslt/extension-function.xsl", body, String.class);
}

@Path("/xslt-custom-uri-resolver")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String customURIResolver(String body) {
return producerTemplate.requestBody("xslt:xslt/include_not_existing_resource.xsl?uriResolver=#customURIResolver", body,
String.class);
}

@Path("/xslt-ref")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String xsltRef(String body) {
return producerTemplate.requestBody("xslt:ref:xslt_resource", body, String.class);
}

@Path("/xslt-bean")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String xsltBean(String body) {
return producerTemplate.requestBody("xslt:bean:xslt_bean.getXsltResource", body, String.class);
}

@Path("/xslt-file")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String xsltFile(String body) {
return producerTemplate.requestBody("xslt:file:src/main/resources/xslt/classpath-transform.xsl", body, String.class);
}

@Path("/xslt-http")
@POST
@Produces(MediaType.TEXT_PLAIN)
public String xsltHttp(String body) {
String serverURL = ConfigProvider.getConfig()
.getConfigValue("xslt.server-url")
.getRawValue();
return producerTemplate.requestBody("xslt:" + serverURL + "/xslt", body, String.class);
}

@Path("/aggregate")
@GET
@Produces(MediaType.TEXT_PLAIN)
public String aggregate() throws Exception {
MockEndpoint mock = camelContext.getEndpoint("mock:transformed", MockEndpoint.class);
mock.expectedMessageCount(1);
camelContext.getRouteController().startRoute("aggregate");

mock.assertIsSatisfied();
return mock.getExchanges().get(0).getIn().getBody(String.class);

}

@Path("/html-transform")
@POST
@Consumes(MediaType.TEXT_HTML)
Expand Down
Expand Up @@ -18,9 +18,18 @@

import org.w3c.dom.Document;

import io.quarkus.runtime.annotations.RegisterForReflection;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.xslt.saxon.XsltSaxonAggregationStrategy;
import org.apache.camel.support.builder.Namespaces;

// These reflections registrations should be removed with fixing https://github.com/apache/camel-quarkus/issues/1615
@RegisterForReflection(classNames = {
"net.sf.saxon.Configuration",
"net.sf.saxon.functions.String_1",
"net.sf.saxon.functions.Tokenize_1",
"net.sf.saxon.functions.StringJoin",
"org.apache.camel.component.xslt.saxon.XsltSaxonBuilder" })
public class XmlRouteBuilder extends RouteBuilder {
public static final String DIRECT_HTML_TRANSFORM = "direct:html-transform";
public static final String DIRECT_HTML_TO_TEXT = "direct:html-to-text";
Expand Down Expand Up @@ -48,5 +57,13 @@ public void configure() {
.split()
.xtokenize("//C:child", new Namespaces("C", "urn:c"))
.to("seda:xtokenize-result");

from("file:src/test/resources?noop=true&sortBy=file:name&antInclude=*.xml")
.routeId("aggregate").noAutoStartup()
.aggregate(new XsltSaxonAggregationStrategy("xslt/aggregate.xsl"))
.constant(true)
.completionFromBatchConsumer()
.log("after aggregate body: ${body}")
.to("mock:transformed");
}
}

0 comments on commit b92f112

Please sign in to comment.