apache / camel-quarkus

Apache Camel Quarkus
https://camel.apache.org
Apache License 2.0
256 stars 191 forks source link

cxf-soap: after upgrade from Quarkus 3.0.3.Final to 3.2.4.Final @QuarkusTest and @QuarkusIntegrationTest tests not working any more #5218

Closed jochenr closed 8 months ago

jochenr commented 1 year ago

Bug description

Hi,

at the moment I'm trying to upgrade a cxf-soap project (using ws-trust, ws-security, mtom, ws-rm) from quarkus 3.0,3.Final to 3.2.4.Final.

Unfortunately my tests do not work any more;-(

The strange thing is that I do not even get an error or exception. Debuggingthe cxf code I see that the test-calll just hangs in the class "org.apache.cxf.endpoint.ClientImpl" in the method "private Object[] doInvoke(....)"

After quarkus hang detection noticed the timeout I get this Stack Trace:

@QuarkusTest has detected a hang, as there has been no test activity in PT10M
To configure this timeout use the quarkus.test.hang-detection-timeout config property
A stack trace is below to help diagnose the potential hang
=== Stack Trace ===
Thread main: TIMED_WAITING
  Stack:
    java.base@17.0.8/java.lang.Object.wait(Native Method)
    java.base@17.0.8/java.io.PipedInputStream.awaitSpace(PipedInputStream.java:273)
    java.base@17.0.8/java.io.PipedInputStream.receive(PipedInputStream.java:231)
    java.base@17.0.8/java.io.PipedOutputStream.write(PipedOutputStream.java:150)
    org.apache.cxf.io.AbstractWrappedOutputStream.write(AbstractWrappedOutputStream.java:51)
    org.apache.cxf.io.AbstractThresholdOutputStream.write(AbstractThresholdOutputStream.java:69)
    com.ctc.wstx.io.UTF8Writer.write(UTF8Writer.java:143)
    com.ctc.wstx.sw.BufferingXmlWriter.writeRaw(BufferingXmlWriter.java:286)
    com.ctc.wstx.sw.BufferingXmlWriter.writeCharacters(BufferingXmlWriter.java:600)
    com.ctc.wstx.sw.BaseStreamWriter.writeCharacters(BaseStreamWriter.java:467)
    org.apache.cxf.staxutils.StaxUtils.copy(StaxUtils.java:759)
    org.apache.cxf.staxutils.StaxUtils.copy(StaxUtils.java:707)
    org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor$SAAJOutEndingInterceptor.handleMessage(SAAJOutInterceptor.java:213)
    org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor$SAAJOutEndingInterceptor.handleMessage(SAAJOutInterceptor.java:173)
    org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
    org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:528)
    org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:439)
    org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:354)
    org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:312)
    org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
    org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:140)
    jdk.proxy5/jdk.proxy5.$Proxy166.checkHealth(Unknown Source)
    de.xxx.xxx.xxx.CxfPlainHealthCheckSyncTest.testWebserviceSync(CxfPlainHealthCheckSyncTest.java:74)
    java.base@17.0.8/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    java.base@17.0.8/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    java.base@17.0.8/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.base@17.0.8/java.lang.reflect.Method.invoke(Method.java:568)
    app//io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:1015)
    app//io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:829)
    app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$191/0x000001de26096fe0.apply(Unknown Source)
    app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
    app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$$Lambda$192/0x000001de260973f0.apply(Unknown Source)
    app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
    app//org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$$Lambda$2157/0x000001de26c663c0.apply(Unknown Source)
    app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    app//org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
    app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
    app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
    app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$191/0x000001de26096fe0.apply(Unknown Source)

Any idea what could be the cause of the issue?

jamesnetherton commented 1 year ago

@ppalaga any thoughts on this?

ppalaga commented 1 year ago

Thanks for the record, @jochenr! Could you please share a minimal reproducer?

jochenr commented 1 year ago

I'll try to write one within the next days

jochenr commented 1 year ago

I spent the whole day on trying to buld an easy reproducer. Unfortunately I can not reproduce the issue yet😥

I tried to extend the https://github.com/jochenr/camel-quarkus-examples/tree/camel-quarkus-main/cxf-soap example with WS-Security for our "SAML Sender Vouches" logic, because the last message I saw in the stacktrace was

INFO  [org.apa.cxf.ws.sec.wss.pol.AbstractBindingBuilder] (main) No CallbackHandler available to retrieve a password. We will now try the crypto properties file for a private password

--> https://github.com/jochenr/camel-quarkus-examples/tree/camel-quarkus-main/cxf-soap

since this works as expected I will continue extending my example app with artemis, pooled-jms, jmx, jta, xa, etc. to reproduce the problem of our real application.

P.S: the one test that's failing in this version "WsdlClientTest#testUpdateCustomer" could show another issue: The code with in

 Awaitility.await().atMost(Duration.ofSeconds(5))
                .until(() -> {
                List<Customer> updatedCustomers = cxfClient.getCustomersByName("test");

seems to have an issue with the CDI context, when using elytron security

jochenr commented 1 year ago

Actually, what are the correct maven coordinates for camel-quarkus?

this:

<dependency>
  <groupId>io.quarkus.platform</groupId>
  <artifactId>quarkus-camel-bom</artifactId>
  <version>3.2.4.Final</version>
  <type>pom</type>
</dependency>

or this:

<dependency>
  <groupId>org.apache.camel.quarkus</groupId>
  <artifactId>camel-quarkus-bom</artifactId>
  <version>3.0.0-RC2</version>
  <type>pom</type>
</dependency>

?

ppalaga commented 1 year ago
<dependency>
  <groupId>io.quarkus.platform</groupId>
  <artifactId>quarkus-camel-bom</artifactId>
  <version>3.2.4.Final</version>
  <type>pom</type>
</dependency>

We recommend the above in combination with

<dependency>
   <groupId>io.quarkus.platform</groupId>
   <artifactId>quarkus-bom</artifactId>
   <version>3.2.4.Final</version>
   <type>pom</type>
</dependency>

That's what you also get from https://code.quarkus.io. Its main advantage is that you can use quarkus tooling to do upgrades and the BOM entries are aligned across all extensions available on code.quarkus.io

<dependency>
  <groupId>org.apache.camel.quarkus</groupId>
  <artifactId>camel-quarkus-bom</artifactId>
  <version>3.0.0-RC2</version>
  <type>pom</type>
</dependency>

This would work as well in combination with

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-bom</artifactId>
   <version>3.2.4.Final</version>
   <type>pom</type>
</dependency>

Note the different groupId io.quarkus This combo is handier when you'd like to test against local builds of Quarkus or Camel Quarkus.

jochenr commented 1 year ago

thank you for clarification. then I did it right in my projects

jochenr commented 1 year ago

I just found the cause of the problem and extended my reproducer: https://github.com/jochenr/camel-quarkus-examples/tree/camel-quarkus-main/cxf-soap

the issue occurs, if you change the example to use https (TLS)

I hope you can reproduce it with my extendend camel-quarkus-example.

Is it a bug or did I overlook some needed configuration change for TLS/SSL in the release notes?

jochenr commented 1 year ago

did you already have a look at my reproducer? can you confirm the behaviour I described?

jochenr commented 1 year ago

today I tried to build a simpler reproducer, based on the official camel-quarkus "integration-test-groups":

https://github.com/jochenr/camel-quarkus/blob/main/integration-test-groups/cxf-soap/cxf-soap-ssl/src/test/java/org/apache/camel/quarkus/component/cxf/soap/ssl/it/PojoClientTest.java

unfortunately this code does not show the issue, but works as expected😥

But it's quite similar to https://github.com/jochenr/camel-quarkus-examples/blob/camel-quarkus-main/cxf-soap/src/test/java/org/acme/cxf/soap/PojoClientTest.java

which reproduces the issue.

My current suspicion is tha the problem only occurs when using a combination of WS-* standard over HTTPS

jochenr commented 1 year ago

I did another reproducer:

https://github.com/jochenr/camel-quarkus/tree/main/integration-test-groups/cxf-soap/cxf-soap-ws-security-server

the idea of this reproducer is to use https://github.com/apache/camel-quarkus/tree/3.2.x/integration-test-groups/cxf-soap/cxf-soap-ws-security-server and extend it to use SSL/https as in https://github.com/apache/camel-quarkus/tree/3.2.x/integration-test-groups/cxf-soap/cxf-soap-ssl

so the combination of the "cxf-soap-ws-security-server" and "cxf-soap-ssl" tests

Now I'm quite sure that the cause of the probloem is using

<dependency>
    <groupId>io.quarkiverse.cxf</groupId>
    <artifactId>quarkus-cxf-rt-ws-security</artifactId>
</dependency>

in an application that uses

<dependency>
    <groupId>org.apache.camel.quarkus</groupId>
    <artifactId>camel-quarkus-cxf-soap</artifactId>
</dependency>

with ssl enabled in application.properties

jamesnetherton commented 1 year ago

I'm not an expert here, but I wonder if it's the same issue as CXF-8895?

There is a workaround mentioned in one of the comments.

ppalaga commented 1 year ago

Sorry for the delay @jochenr, I was busy investigating a similar issues https://github.com/quarkiverse/quarkus-cxf/issues/973 and https://github.com/quarkiverse/quarkus-cxf/issues/989 in Quarkus CXF. I'll look closer on your reproducers on monday.

jochenr commented 1 year ago

Hello @jamesnetherton, thank you for the hint. Unffortunately mentioned workaround

((BindingProvider) port).getRequestContext().put("force.urlconnection.http.conduit", "true");

did not solve the issue for me

ppalaga commented 1 year ago

@jochenr I was able to fix your reproducer. The root issue is that you need to set the trust store on the client before it accesses the WSDL - that's before Service.create().

One possible way to achieve that is via HTTPConduitConfigurer set on CXF Bus. I see two ways how to do that:

A. In a static block of your test - see https://github.com/jochenr/camel-quarkus-examples/pull/1,

B. Inside your application - see https://github.com/jochenr/camel-quarkus-examples/pull/2

https://github.com/jochenr/camel-quarkus-examples/pull/2 also shows how I found the issue: in the first commit I switched to URLConnectionHTTPConduit which has thrown a much more useful error message stating

Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
[INFO] [stdout]         at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
[INFO] [stdout]         at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:371)
[INFO] [stdout]         at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:314)
[INFO] [stdout]         at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:309)
[INFO] [stdout]         at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1357)
[INFO] [stdout]         at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1232)
[INFO] [stdout]         at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1175)
[INFO] [stdout]         at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
[INFO] [stdout]         at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:480)
[INFO] [stdout]         at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:458)
[INFO] [stdout]         at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:201)
[INFO] [stdout]         at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
[INFO] [stdout]         at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1505)
[INFO] [stdout]         at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1420)
[INFO] [stdout]         at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
[INFO] [stdout]         at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
[INFO] [stdout]         at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:580)
[INFO] [stdout]         at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:187)
[INFO] [stdout]         at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1430)
[INFO] [stdout]         at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1401)
[INFO] [stdout]         at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:220)
[INFO] [stdout]         at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.setupWrappedStream(URLConnectionHTTPConduit.java:274)
[INFO] [stdout]         at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleHeadersTrustCaching(HTTPConduit.java:1370)
[INFO] [stdout]         at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.onFirstWrite(HTTPConduit.java:1331)
[INFO] [stdout]         at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.onFirstWrite(URLConnectionHTTPConduit.java:307)
[INFO] [stdout]         at org.apache.cxf.io.AbstractWrappedOutputStream.write(AbstractWrappedOutputStream.java:47)
[INFO] [stdout]         at org.apache.cxf.io.AbstractThresholdOutputStream.unBuffer(AbstractThresholdOutputStream.java:89)
[INFO] [stdout]         at org.apache.cxf.io.AbstractThresholdOutputStream.write(AbstractThresholdOutputStream.java:63)
[INFO] [stdout]         at com.ctc.wstx.io.UTF8Writer.flush(UTF8Writer.java:100)
[INFO] [stdout]         at com.ctc.wstx.sw.BufferingXmlWriter.flush(BufferingXmlWriter.java:242)
[INFO] [stdout]         at com.ctc.wstx.sw.BaseStreamWriter.flush(BaseStreamWriter.java:260)
[INFO] [stdout]         ... 84 more
[INFO] [stdout] Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
[INFO] [stdout]         at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439)
[INFO] [stdout]         at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306)
[INFO] [stdout]         at java.base/sun.security.validator.Validator.validate(Validator.java:264)
[INFO] [stdout]         at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
[INFO] [stdout]         at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
[INFO] [stdout]         at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1341)
[INFO] [stdout]         ... 110 more
[INFO] [stdout] Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
[INFO] [stdout]         at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
[INFO] [stdout]         at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
[INFO] [stdout]         at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
[INFO] [stdout]         at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434)
[INFO] [stdout]         ... 115 more

That made quite clear where to look.

I hope this will also fix your production application?

jochenr commented 1 year ago

Hello @ppalaga, thank you for your efforts. And sorry for the bad reproducer.

Unfortunately this won't fix our real/production application. In the real example I use a local WSDL Fle via Thread.currentThread().getContextClassLoader().getResource("HelloService.wsdl")

I'll provide a better reproducer. Sorry for that bug

The one IU started here https://github.com/jochenr/camel-quarkus/tree/main/integration-test-groups/cxf-soap/cxf-soap-ws-security-server isn't finished yet. I have to slim it and make it independant of the the POM hierarchy to make it easier to switch between 3.0.3.Final and 3.2.5.Final. And to switch between HTTP and HTTPS.

ppalaga commented 1 year ago

I have to slim it and make it independant of the the POM hierarchy to make it easier to switch between 3.0.3.Final and 3.2.5.Final

To compare the behavior between 3.2 and 3.0, I'd suggest to implement your reproducer in Camel Quarkus 3.2.x branch. Having it there you can test it against the older Quarkus Platfrom BOMs by setting these properties on the command line: https://github.com/apache/camel-quarkus/blob/main/poms/build-parent-it/pom.xml#L39-L45

jochenr commented 1 year ago

I saw you tipp too loate.

I just made it independant of the parent POm structure

https://github.com/jochenr/camel-quarkus/tree/main/integration-test-groups/cxf-soap/cxf-soap-ws-security-server

Unfortunately the reproducer doesn't show the behaviour that I want too see, but runs perfectly well😥

Now I have to deceide, if I add piece by piece the logic of my real application or if I slim my real application to the point the error disappears

ppalaga commented 1 year ago

@jochenr I feel your pain, creating a minimal reproducer is often the hardest part.

ppalaga commented 1 year ago

BTW, to be able to exclude two issues known in Quarkus CXF 2.2.2, you may want to try

  1. Switch to URLConnectionHTTPConduit https://github.com/jochenr/camel-quarkus-examples/pull/1/commits/ef8478286df439156006ea0fcf85f2fb4f7843e9
  2. Force vert.x 4.4.5 https://github.com/quarkiverse/quarkus-cxf/pull/993/commits/f2c0f2895d459219008bbff677f435f8f1aa3b26
jochenr commented 1 year ago

Hello @ppalaga,

I think I finally managed to build a "working" reproducer: https://github.com/jochenr/my-camel-quarkus-projects/tree/main/camel-quarkus-cxf-soap-wssecurity

Running

mvn clean install

on the project you should see something like this that showes that the test hangs

2023-09-06 16:55:06,780 INFO  [org.apa.cxf.ws.sec.wss.pol.AbstractBindingBuilder] (main) No CallbackHandler available to retrieve a password. We will now try the crypto properties file for a private password
@QuarkusTest has detected a hang, as there has been no test activity in PT30S
To configure this timeout use the quarkus.test.hang-detection-timeout config property
A stack trace is below to help diagnose the potential hang
=== Stack Trace ===
Thread main: TIMED_WAITING
  Stack:
    java.base@17.0.8/java.lang.Object.wait(Native Method)
    java.base@17.0.8/java.io.PipedInputStream.awaitSpace(PipedInputStream.java:273)
    java.base@17.0.8/java.io.PipedInputStream.receive(PipedInputStream.java:231)
    java.base@17.0.8/java.io.PipedOutputStream.write(PipedOutputStream.java:150)

If you take a look at https://github.com/jochenr/my-camel-quarkus-projects/blob/main/camel-quarkus-cxf-soap-wssecurity/quarkus-root/src/test/java/de/jochenr/integration/contact/BaseTest.java you see that I commented out lines 66 to 87 this code block:

    // static {
    //     HTTPConduitConfigurer httpConduitConfigurer = new HTTPConduitConfigurer() {
    //         public void configure(String name, String address, HTTPConduit c) {

    //             TrustManager[] trustManagers = createTrustManagers();

    //             TLSClientParameters tlsCP = new TLSClientParameters();
    //             tlsCP.setTrustManagers(trustManagers);

    //             // other TLS/SSL configuration like setting up TrustManagers
    //             // in case of "localhost" the certname does not match the hostname, so ignore it
    //             tlsCP.setDisableCNCheck(isLocalhost(c));
    //             tlsCP.setUseHttpsURLConnectionDefaultSslSocketFactory(false);

    //             c.setTlsClientParameters(tlsCP);

    //         }
    //     };

    //     final Bus bus = BusFactory.getThreadDefaultBus();
    //     bus.setExtension(httpConduitConfigurer, HTTPConduitConfigurer.class);
    // }

if you uncomment that code-block the test is running as it should.

BUT:

In my real application I do not have self-signed certificates. I expect do not need any code for the truststore, because our (custom) Root-CA is registered in the "cacerts" of our Java installations. So all of your certificates should be trusted "out of the box".

I don't want to have that static block in our code.....

The behaviour is really different from Quarkus 3.0.3,Final. Because if you switch to that version the example is not working any more?!? 3.0.3.Final gives that Exception:

Caused by: [CIRCULAR REFERENCE: com.ctc.wstx.exc.WstxIOException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]

Can you please have a look?

Thank you Jochen

ppalaga commented 1 year ago

Thanks for the new reproducer and sorry for the delay, @jochenr.

I was able to reproduce all you mention, but at the first sight, it seems expected: if you do not tell the client which certs it should trust, then it cannot connect. Of course it should throw a clear exception, something like PKIX path building failed you report with 3.0.3, rather than hang. I'll check this aspect with newer quarkus and quarkus-cxf tomorrow.

So if your production env. behaves the same as test env without trust store set, then I'd say either the default java truststore is ignored for some reason, or your default java truststore is different than you think. I'll perform some experiments tomorrow to see which one is the case.

jochenr commented 1 year ago

Hi @ppalaga ,

now I know what is happening and what's causing the issue.

I initialize my service with that code

        final URL serviceUrl = Thread.currentThread().getContextClassLoader().getResource("wsdl/ContactService.wsdl");
        final Service service = Service.create(serviceUrl, ContactService.SERVICE, new AddressingFeature(true, true), new WSRMConfigRMFeature());

        ContactWS port = service.getPort(ContactWS.class);
        BindingProvider bp = (BindingProvider) port;

        Map<String, Object> requestContext = bp.getRequestContext();

        // set target address
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, getServerHttpUrl() + WS_BASE_PATH);

So, I'm not using the onlilne WSDL, but one that is packaged inside my application.

The WSDL has a default address location inside the port definition of the service definition like that:

    <wsdl:service name="ContactService">
        <wsdl:port binding="tns:ContactServiceSoapBinding" name="ContactServicePort">
            <!-- working -->
            <!--
            <soap:address location="http://localhost:8180/cxfservices/contact" />
            -->

            <!-- also working -->
            <!--
            <soap:address location="https://localhost:8543/cxfservices/contact" />
            -->

            <!-- NOT Working, because override from code does not work any more! -->

            <soap:address location="https://nowhere.com:8080/mustgetoverridden" />

        </wsdl:port>
    </wsdl:service>

For that default address I always set an URL that is not existing, so thet the application is forced to override it. I want to prevent that someone is calling the service for a wrong staging-level or a wrong tenant.

Meanwhilem I noticed that CXF is trying to use the address from the WSDL instead of the one I set in the code with:

requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, getServerHttpUrl() + WS_BASE_PATH);

I built two additional reproducers:

https://github.com/jochenr/my-camel-quarkus-projects/tree/main/camel-quarkus-cxf-soap-wssecurity-wsrm and https://github.com/jochenr/my-camel-quarkus-projects/tree/main/camel-quarkus-cxf-soap-wsrm

You can make them both work by just changing the

    <wsdl:service name="ContactService">
        <wsdl:port binding="tns:ContactServiceSoapBinding" name="ContactServicePort">           
            <soap:address location="...." />
        </wsdl:port>
    </wsdl:service>

mentionad above.

The https://github.com/jochenr/my-camel-quarkus-projects/tree/main/camel-quarkus-cxf-soap-wssecurity-wsrm example shows also the problem that the test just hangs.

The https://github.com/jochenr/my-camel-quarkus-projects/tree/main/camel-quarkus-cxf-soap-wsrm example at least shows the issue and fals.

Another thing I noticed: If you look at the test class(es) I print out the line

logger.info("SOAP Call from ContactTest will call:\t" + getServerHttpUrl() + WS_BASE_PATH);

in the constructor. If you look at the console you see that this line is printed out 6 times. Why??

Hope you now have a better starting point to fix the bug. It should be in CXF, not in Camel nor in Quarkus, isn't it?

Thank you & best regards,

jochenr commented 1 year ago

Hello @ppalaga ,

I just want to update this issue by providing the findings we made (together with Red Hat Support):

My reproducers actually show several issues:

1.) CXF configuration issue with HTTPConduit

        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                }

                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                }

            } };

            TLSClientParameters tlsCP = new TLSClientParameters();
            tlsCP.setDisableCNCheck(true);
            tlsCP.setHostnameVerifier(new NoopHostnameVerifier());

    // this is needed with new version
    // is this just a workaround or does it make sense, if I set a "NoopHostnameVerifier" ?
            tlsCP.setTrustManagers(trustAllCerts);

            httpConduit.setTlsClientParameters(tlsCP);

same for an CxfEndpoint as producer in Quarkus camel-cxf:

            CxfEnpoint mockToSapCxfProducer;
            ....

            /* this is now need start*/
            SSLContextParameters sslContextParameters = new SSLContextParameters();
            TrustManagersParameters trustManagersParameters = new TrustManagersParameters();
            trustManagersParameters.setTrustManager(trustAllCerts[0]);
            sslContextParameters.setTrustManagers(trustManagersParameters);
            /* this is now needs end*/
            mockToSapCxfProducer.setSslContextParameters(sslContextParameters);

            mockToSapCxfProducer.setHostnameVerifier(new NoopHostnameVerifier());

2.) Order/place where to get the HTTPConduit

You have to get the HTTPConduit

            HTTPConduit httpConduit = (HTTPConduit) ClientProxy.getClient(port).getConduit();

AFTER putting ENDPOINT_ADDRESS_PROPERTY did not work in my real application.

Otherwise the default Endpoint of the WSDL is used instead of the one provided with "requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY...." Then it's the same as the WS-RM Problem (see 3.)

this works:

        ContactWS port = service.getPort(ContactWS.class);
                BindingProvider bp = (BindingProvider) port;
        Map<String, Object> requestContext = bp.getRequestContext();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://myhost/mywebservice");
        HTTPConduit httpConduit = (HTTPConduit) ClientProxy.getClient(port).getConduit();
        //....do something with the conduit....like describes in 1.)

this DOES NOT work any more with new version:

        ContactWS port = service.getPort(ContactWS.class);
                BindingProvider bp = (BindingProvider) port;
        HTTPConduit httpConduit = (HTTPConduit) ClientProxy.getClient(port).getConduit();
        //....do something with the conduit....like describes in 1.)
        Map<String, Object> requestContext = bp.getRequestContext();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://myhost/mywebservice");

See here: https://github.com/jochenr/my-camel-quarkus-projects/blob/9d6f45be41eea419cf23cff99b62620eecdb7b75/camel-quarkus-cxf-soap-wssecurity/quarkus-root/src/test/java/de/jochenr/integration/contact/CxfClientSyncTest.java#L82

3.) WS-RM /WS-ReliableMessabingis always using the default URL from the WSDL file.

See: https://github.com/jochenr/my-camel-quarkus-projects/tree/main/camel-quarkus-cxf-soap-wsrm and my previous comment
https://github.com/apache/camel-quarkus/issues/5218#issuecomment-1730918810 and https://github.com/quarkiverse/quarkus-cxf/issues/1061

You cannot override it with "requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://myhost/mywebservice");" in a client using jax-ws api.

And you also cannot override it in an producer CxfEndpoint

CxfEnpoint mockToSapCxfProducer = new CxfProducerEndpointBuilder("https://myhost/mywebservice", context);
....

https://github.com/jochenr/my-camel-quarkus-projects/blob/9d6f45be41eea419cf23cff99b62620eecdb7b75/camel-quarkus-cxf-soap-wsrm/quarkus-root/src/test/java/de/jochenr/integration/contact/ContactTest.java#L114 calls a Rest endoint from there the camel route uses a CxfProducer to call the backend https://github.com/jochenr/my-camel-quarkus-projects/blob/9d6f45be41eea419cf23cff99b62620eecdb7b75/camel-quarkus-cxf-soap-wsrm/quarkus-root/src/main/java/de/jochenr/quarkus/integration/contact/route/AsyncRouteBuilder.java#L101

4.) QuarkusTest doesn't show exceptions in the cases above, but is just hanging. This made finding the issues above quite hard....

I think the root issues of 1.) - 3.) is inside Cxf and should be fixed there.

Since my employer is Red Hat Customer we continue working on the issues using the Red Hat Support portal. We need all of this (and more) fixed before the next release of "Red Hat Build of Camel-Quarkus".

Should we leave this open until everything is fixed or should we close it, because we contiue to work on it with Red Hat Support?

Regards, Jochen

jochenr commented 8 months ago

Since version 3.2.9 everything woks again as expected/as before