Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't use XSLT feature in native mode #501

Open
Ballsigno opened this issue Aug 19, 2022 · 14 comments
Open

Can't use XSLT feature in native mode #501

Ballsigno opened this issue Aug 19, 2022 · 14 comments

Comments

@Ballsigno
Copy link

Ballsigno commented Aug 19, 2022

Basic Info

Java: java 17.0.4 2022-07-19 LTS (oracle)
Mandrel: openjdk 17.0.3 2022-04-19
※ I know the doc says Native mode is currently supported for Java 11.

Hi. I have a question about XSLT feature.

// get my client
Client client = ClientProxy.getClient(myService);

// create XSLT InInterceptor (from ~/resources/test.xsl)
XSLTInInterceptor inInterceptor = new XSLTInInterceptor("test.xsl");

// add to my client settings
client.getInInterceptors().add(inInterceptor);

↑ It works fine in dev mode, but when I tried in native mode, it was failed.

java.lang.IllegalArgumentException: Cannot create XSLT template from path: test.xsl
        at org.apache.cxf.feature.transform.AbstractXSLTInterceptor.<init>(AbstractXSLTInterceptor.java:74)
        at org.apache.cxf.feature.transform.XSLTInInterceptor.<init>(XSLTInInterceptor.java:51)
        at org.acme.GreetingResource.test1(GreetingResource.java:47)
        at org.acme.GreetingResource$quarkusrestinvoker$test1_7ef637907f0bfe03310c5187ae0e45fdc512a9ab.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:108)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:140)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:555)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.lang.Thread.run(Thread.java:833)
        at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704)
        at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)
Caused by: javax.xml.transform.TransformerConfigurationException: java.lang.NullPointerException
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:1077)
        at org.apache.cxf.feature.transform.AbstractXSLTInterceptor.<init>(AbstractXSLTInterceptor.java:71)
        ... 15 more
Caused by: java.lang.NullPointerException
        at com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg.getFileName(ErrorMsg.java:264)
        at com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg.<init>(ErrorMsg.java:237)
        at com.sun.org.apache.xalan.internal.xsltc.compiler.Parser.makeInstance(Parser.java:1000)
        at com.sun.org.apache.xalan.internal.xsltc.compiler.Parser.startElement(Parser.java:1312)
        at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2SAX.parse(DOM2SAX.java:269)
        at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2SAX.parse(DOM2SAX.java:192)
        at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2SAX.parse(DOM2SAX.java:130)
        at com.sun.org.apache.xalan.internal.xsltc.compiler.Parser.parse(Parser.java:439)
        at com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC.compile(XSLTC.java:478)
        at com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC.compile(XSLTC.java:572)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:1036)
        ... 16 more

If the xsl file doesn't exist, I'm sure I will get 'Cannot load XSLT from path:" error.
Therefore I passed the right argument but failed.
Is this a bug or am I missing something?

@shumonsharif
Copy link
Contributor

Hi @Ballsigno Did you include the test.xsl resource in your native image?
https://quarkus.io/guides/writing-native-applications-tips#including-resources

@Ballsigno
Copy link
Author

@shumonsharif
Thank you for your reply!
Yes, I have pom.xml that has include setting.

<profile>
~ omit ~
  <properties>
    <skipITs>false</skipITs>
    <quarkus.package.type>native</quarkus.package.type>
    <quarkus.native.additional-build-args>-H:IncludeResources=test.xsl</quarkus.native.additional-build-args>
  </properties>

Additionally, I have tried this in native mode, and I successfully got the input stream.
(But somehow, XSLTInInterceptor doesn't work.)

InputStream testStream = ClassLoaderUtils.getResourceAsStream("test.xsl", this.getClass());
Log.info(testStream.available()); // has value

@shumonsharif
Copy link
Contributor

Likely a bug; thanks for opening the issue! Would you be able to provide a small reproducer?

Ballsigno pushed a commit to Ballsigno/quarkus-cxf-xslt-test that referenced this issue Aug 20, 2022
@Ballsigno
Copy link
Author

Ballsigno commented Aug 20, 2022

Alright, I created the small one.
(sorry, I didn't know the message is displayed if I put a link at my push comment.)

I really want to know whether this can be fixed in a few days or not.
Let me know if you need anything.

https://github.com/Ballsigno/quarkus-cxf-xslt-test/tree/master/quarkus-cxf-xslt-test

@shumonsharif
Copy link
Contributor

Thank you so much @Ballsigno ! This helps tremendously.

Unfortunately, debugging native issues always takes quite a bit of time. Not sure at this stage if this will be a quick thing or not, though I'm doubtful it'll be fixed and released in a few days. Will certainly prioritize this issue for you, and will keep you posted on progress.

@Ballsigno
Copy link
Author

@shumonsharif
Thank you for your support! It's understandable.
I'm researching whether I can use this for my purpose these days, and now I can see the silver lining. If it turned out to be complicated, I will wait patiently🙏

@ppalaga
Copy link
Contributor

ppalaga commented Aug 23, 2022

Making XSLT work in native mode is indeed tricky. Camel Quarkus supports it via its camel-quarkus-xslt extension. It is tested there only in Camel related scenarios, but I think it might work for you as well. What I think you should do is the following:

  1. Go to https://code.quarkus.io/?e=org.apache.camel.quarkus%3Acamel-quarkus-xslt&extension-search=origin:platform%20camel-quarkus-xslt
  2. Generate the project zip, unpack locally, take the properties, dependencyManagement and dependencies from there and add them into your project.
  3. Make sure that Quarkus version from code.quarkus.io is compatible with quarkus-cxf version you use. If you wait a week or so, the versions that should work well with each other are Quarkus 2.12.0.Final, Camel Quarkus 2.12.0 (managed in Quarkus Platform BOM 2.12.0.Final, so you actually do not need to care for the Camel Quarkus version) and quarkus-cxf 1.15.0
    Starting with 1.15.0, Quarkus CXF will also have a BOM io.quarkiverse.cxf:quarkus-cxf-bom that you should import into your project
  4. Configure quarkus.camel.xslt.sources in your application.properties - see https://camel.apache.org/camel-quarkus/2.11.x/reference/extensions/xslt.html
  5. Report whether it worked for you

@Ballsigno
Copy link
Author

Thanks for clear steps instructions🙇🏻‍♂️
I’m not familiar with those things. Therefore I’m not so sure how is that related to my problem. Anyway, I'll give it a try tomorrow or the day after tomorrow.

@ppalaga
Copy link
Contributor

ppalaga commented Aug 23, 2022

not so sure how is that related to my problem

To make Java code work in native mode, you need to provide some (sometimes a lot of) GraalVM configuration. That's what Quarkus extensions do (among other things). camel-quarkus-xslt does it for XSLT quite generally, although its main aim is to support Camel integration pipelines using XSLT.

@Ballsigno
Copy link
Author

Ballsigno commented Aug 24, 2022

Thank you for your explanation.
I tried it Java 11 and 17, but very unfortunately, It didn't work for me.
(I created a branch based on the reproducer)

Indeed it seems it affects XSLT feature because the error message is changed. (But my xsl file is valid format.)

java.lang.IllegalArgumentException: Cannot create XSLT template from path: test.xsl
        at org.apache.cxf.feature.transform.AbstractXSLTInterceptor.<init>(AbstractXSLTInterceptor.java:74)
        at org.apache.cxf.feature.transform.XSLTInInterceptor.<init>(XSLTInInterceptor.java:51)
        at org.acme.GreetingResource.test1(GreetingResource.java:47)
        at org.acme.GreetingResource$quarkusrestinvoker$test1_7ef637907f0bfe03310c5187ae0e45fdc512a9ab.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:108)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:140)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:555)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.lang.Thread.run(Thread.java:833)
        at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704)
        at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)
Caused by: javax.xml.transform.TransformerConfigurationException: Could not compile stylesheet
        at org.apache.xalan.xsltc.trax.TransformerFactoryImpl.newTemplates(TransformerFactoryImpl.java:832)
        at org.apache.camel.quarkus.support.xalan.XalanTransformerFactory.newTemplates(XalanTransformerFactory.java:70)
        at org.apache.cxf.feature.transform.AbstractXSLTInterceptor.<init>(AbstractXSLTInterceptor.java:71)
        ... 15 more

(add) The real problem is ↓

java.lang.NoClassDefFoundError: org.apache.xalan.xsltc.compiler.ObjectFactory
    at org.apache.xalan.xsltc.compiler.ObjectFactory.class$(ObjectFactory.java:292)
    at org.apache.xalan.xsltc.compiler.ObjectFactory.findClassLoader(ObjectFactory.java:410)
    at org.apache.xalan.xsltc.compiler.Parser.makeInstance(Parser.java:925)
    at org.apache.xalan.xsltc.compiler.Parser.startElement(Parser.java:1250)
    at org.apache.xalan.xsltc.trax.DOM2SAX.parse(DOM2SAX.java:285)
    at org.apache.xalan.xsltc.trax.DOM2SAX.parse(DOM2SAX.java:212)
    at org.apache.xalan.xsltc.trax.DOM2SAX.parse(DOM2SAX.java:149)
    at org.apache.xalan.xsltc.compiler.Parser.parse(Parser.java:421)
    at org.apache.xalan.xsltc.compiler.XSLTC.compile(XSLTC.java:347)
    at org.apache.xalan.xsltc.compiler.XSLTC.compile(XSLTC.java:446)

Ballsigno pushed a commit to Ballsigno/quarkus-cxf-xslt-test that referenced this issue Aug 25, 2022
@Ballsigno
Copy link
Author

Ballsigno commented Aug 27, 2022

Hmm. I'm totally stuck on this.

Apache Camel seems create a factory and compile a xsl file in initialization process, and XsltBuilder retains the result(template). On the other hand, CXF does similar things after starting application.(When I create XSLTInInterceptor.) I don't know why it doesn't work though.
Since there is AbstractPhaseInterceptor, I can create own XSLTinterceptor, but I could not take the template which is created by Apache Camel.

Is there any other way to fix this? I would really appreciate any information.

@Ballsigno
Copy link
Author

Finally, I found a workaround! Thank you @ppalaga, I wouldn't have found this approach by myself.
I don't know what's going on but, using quarkus camel resources work.
I created own interceptor, and avoid a default action that caused the problem.

Instated of doing this...

// AbstractXSLTInterceptor.java
try {
    InputStream xsltStream = ClassLoaderUtils.getResourceAsStream(xsltPath, this.getClass());
    if (xsltStream == null) {
        throw new IllegalArgumentException("Cannot load XSLT from path: " + xsltPath);
    }
    Document doc = StaxUtils.read(xsltStream);
    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    try {
        transformerFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
    } catch (javax.xml.transform.TransformerConfigurationException ex) {
      //
    }
    xsltTemplate = transformerFactory.newTemplates(new DOMSource(doc)); // Somehow failed!!
} catch (TransformerConfigurationException | XMLStreamException e) {
    throw new IllegalArgumentException(
        String.format("Cannot create XSLT template from path: %s", xsltPath), e);
}

// Inject and pass component as argument from caller.
@Inject
XsltComponent xslt;

// CustomInterceptor
try (XsltEndpoint endPoint = (XsltEndpoint) xslt.createEndpoint("xslt://" + xsltPath)) {
    endPoint.init();
    xsltTemplate = endPoint.getXslt().getTemplate();
} catch (Exception e) {
    throw new IllegalArgumentException(
        String.format("Cannot create XSLT template from path: %s", xsltPath), e);
}

@shumonsharif
I think I'll close this issue for now. is it okay? (I assume this is low priority...)
(I'll be happy if the XSLT feature is officially supported though.)

@ppalaga
Copy link
Contributor

ppalaga commented Aug 29, 2022

I think I'll close this issue for now.

I'd vote for keeping this issue open, as it still does not work flawlessly. This issue also contains a lot of useful information (thanks, @Ballsigno !).

I think our general aim should be to have a general purpose Quarkus XSLT extension that could be leveraged for both Camel and CXF use cases. I need to speak with Quarkus team whether they'd be ready to host it within quarkusio/quarkus repo.

@dufoli
Copy link
Contributor

dufoli commented May 4, 2023

According error it mean that we need a ReflectiveClassBuildItem for class org.apache.xalan.xsltc.compiler.ObjectFactory

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants