Chapter 6. Quarkus CXF user guide This chapter provides information about Quarkus CXF usage and configuration. 6.1. User guide This User guide explains typical use cases of Quarkus CXF. You may want to start with some of the following topics: Create a new project Your first SOAP Web service Your first SOAP Client 6.2. Create a new project This guide explains how to set up a new project for a Quarkus application hosting a CXF client or server or both. 6.2.1. Prerequisites Read the Prerequisites section of Quarkus getting started guide. In addition to that, you may need GraalVM with the native-image command installed and the GRAALVM_HOME environment variable set. See Building a native executable section of the Quarkus documentation. If you are on Linux, a container runtime like docker is sufficient for the native mode too. Use -Pnative -Dquarkus.native.container-build=true instead of -Pnative if you choose this option. 6.2.2. Create project New project skeletons can be generated using code.quarkus.redhat.com . Here you can select the extensions that you want to work with. For a simple Hello world Web service or client the quarkus-cxf extension is enough. Click the blue Generate your application button to download a basic skeleton project. Unpack the zip file and import the project the into your favorite IDE. 6.2.3. Dependency management Quarkus CXF is a part of Quarkus Platform since Quarkus Platform version 3.1.0.Final. Among other things, this means that code.quarkus.redhat.com and other Quarkus development tools generate projects with proper dependency management: <project ...> ... <properties> ... <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version><!-- Check the latest https://repo1.maven.org/maven2/io/quarkus/platform/quarkus-cxf-bom/ --></quarkus.platform.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>USD{quarkus.platform.artifact-id}</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>quarkus-cxf-bom</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ... You should always take care to import the same version of io.quarkus.platform:quarkus-bom and io.quarkus.platform:quarkus-cxf-bom into your project. That's the most reliable way to get compatible versions of Quarkus, CXF, Quarkus CXF and all their transitive dependencies. 6.2.4. Where to go We recommend to proceed with any of the following chapters: Your first SOAP Web service ug_first-soap-client[Your first SOAP Client] 6.3. Your first SOAP Web service on Quarkus In this guide we explain how to create a Quarkus application exposing a simple SOAP Web service. Create project first Follow the Project creation guide before proceeding here. 6.3.1. Hello world! Web service Having the pom.xml in place, you can add a simple Hello world! Web service in src/main/java . Code examples The sample code snippets used in this section come from the server integration test in the source tree of Quarkus CXF First add the service interface: HelloService.java package io.quarkiverse.cxf.it.server; import jakarta.jws.WebMethod; import jakarta.jws.WebService; /** * The simplest Hello service. */ @WebService(name = "HelloService", serviceName = "HelloService") public interface HelloService { @WebMethod String hello(String text); } and then the implementation: HelloServiceImpl.java package io.quarkiverse.cxf.it.server; import jakarta.jws.WebMethod; import jakarta.jws.WebService; /** * The simplest Hello service implementation. */ @WebService(serviceName = "HelloService", portName = "HelloServicePort") public class HelloServiceImpl implements HelloService { @WebMethod @Override public String hello(String text) { return "Hello " + text + "!"; } } For the implementation to get exposed under a certain path, you need to add the following configuration to application.properties : # The context path under which all services will be available quarkus.cxf.path = /soap # Publish "HelloService" under the context path /USD{quarkus.cxf.path}/hello quarkus.cxf.endpoint."/hello".implementor = io.quarkiverse.cxf.it.server.HelloServiceImpl quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature Tip All configuration properties are documented in the Configuration properties reference. With these files in place, you can start Quarkus in dev mode : USD mvn quarkus:dev This will compile the project and start the application on the background. You can test the service using curl or some other SOAP client. First let's have a look at the auto-generated WSDL under http://localhost:8080/soap/hello?wsdl : USD curl http://localhost:8080/soap/hello?wsdl <?xml version='1.0' encoding='UTF-8'?> <wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://server.it.cxf.quarkiverse.io/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="HelloService" targetNamespace="http://server.it.cxf.quarkiverse.io/"> <wsdl:types> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://server.it.cxf.quarkiverse.io/" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://server.it.cxf.quarkiverse.io/"> <xsd:element name="hello" type="tns:hello"/> <xsd:complexType name="hello"> <xsd:sequence> <xsd:element minOccurs="0" name="arg0" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:element name="helloResponse" type="tns:helloResponse"/> <xsd:complexType name="helloResponse"> <xsd:sequence> <xsd:element minOccurs="0" name="return" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema> </wsdl:types> <wsdl:message name="helloResponse"> <wsdl:part element="tns:helloResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="hello"> <wsdl:part element="tns:hello" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:portType name="HelloService"> <wsdl:operation name="hello"> <wsdl:input message="tns:hello" name="hello"> </wsdl:input> <wsdl:output message="tns:helloResponse" name="helloResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="HelloServiceSoapBinding" type="tns:HelloService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="hello"> <soap:operation soapAction="" style="document"/> <wsdl:input name="hello"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="helloResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="HelloService"> <wsdl:port binding="tns:HelloServiceSoapBinding" name="HelloServicePort"> <soap:address location="http://localhost:8080/soap/hello"/> </wsdl:port> </wsdl:service> </wsdl:definitions> Second, let's send a SOAP request to the service: USD curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" \ -d \ '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body><ns2:hello xmlns:ns2="http://server.it.cxf.quarkiverse.io/"><arg0>World</arg0></ns2:hello></soap:Body> </soap:Envelope>' \ http://localhost:8080/soap/hello <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns1:helloResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <return>Hello World!</return> </ns1:helloResponse> </soap:Body> </soap:Envelope> You can see the expected <return>Hello World!</return> in the SOAP response. 6.3.2. Add the logging feature while dev mode is running Sometimes it may come in handy to be able to inspect the SOAP messages received or sent by the server or client. This is easily doable by adding the quarkus-cxf-rt-features-logging extension to pom.xml . Tip Try to do that while Quarkus dev mode is running. You should see the application being recompiled and redeployed upon saving your changes in the source tree. Add this to pom.xml <dependency> <groupId>io.quarkiverse.cxf</groupId> <artifactId>quarkus-cxf-rt-features-logging</artifactId> </dependency> Enable SOAP payload logging in application.properties quarkus.cxf.endpoint."/hello".features=org.apache.cxf.ext.logging.LoggingFeature After that you can send a new SOAP request and see some SOAP payloads in the application console: 2023-01-11 22:12:21,315 INFO [org.apa.cxf.ser.Hel.REQ_IN] (vert.x-worker-thread-0) REQ_IN Address: http://localhost:8080/soap/hello HttpMethod: POST Content-Type: text/xml;charset=UTF-8 ExchangeId: af10747a-8477-4c17-bf5f-2a4a3a95d61c ServiceName: HelloService PortName: HelloServicePort PortTypeName: HelloService Headers: {Accept=*/*, User-Agent=curl/7.79.1, content-type=text/xml;charset=UTF-8, Host=localhost:8080, Content-Length=203, x-quarkus-hot-deployment-done=true} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body><ns2:hello xmlns:ns2="http://server.it.cxf.quarkiverse.io/"><arg0>World</arg0></ns2:hello></soap:Body> </soap:Envelope> 2023-01-11 22:12:21,327 INFO [org.apa.cxf.ser.Hel.RESP_OUT] (vert.x-worker-thread-0) RESP_OUT Address: http://localhost:8080/soap/hello Content-Type: text/xml ResponseCode: 200 ExchangeId: af10747a-8477-4c17-bf5f-2a4a3a95d61c ServiceName: HelloService PortName: HelloServicePort PortTypeName: HelloService Headers: {} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns1:helloResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"><return>Hello World!</return></ns1:helloResponse></soap:Body></soap:Envelope> 6.3.3. Further steps You may want to proceed with packaging your application for running on a JVM or natively . 6.4. Your first SOAP Client on Quarkus In this guide we explain how to create a simple Quarkus application acting as a client of a remote Web service. Create project first Follow the Project creation guide before proceeding here. 6.4.1. Remote Web service for testing First, we need some remote Web service to connect to. We can use a simple Calculator Web service running in a container for that purpose. USD docker run -p 8082:8080 quay.io/l2x6/calculator-ws:1.0 Once the container is up and running, we can inspect its WSDL USD curl -s http://localhost:8082/calculator-ws/CalculatorService?wsdl <?xml version="1.0" ?> <wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="CalculatorService" targetNamespace="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator"> ... <wsdl:binding name="CalculatorServiceSoapBinding" type="tns:CalculatorService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding> <wsdl:operation name="add"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="add"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="addResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> <wsdl:operation name="subtract"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="subtract"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="subtractResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> ... </wsdl:binding> ... </wsdl:definitions> As you can see in the WSDL, the service offers some basic arithmetic operations, such as add , subtract , etc. Let's test it with curl : USD curl -s \ -X POST \ -H "Content-Type: text/xml;charset=UTF-8" \ -d \ '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <add xmlns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator"> <arg0 xmlns="">7</arg0> 1 <arg1 xmlns="">4</arg1> </add> </Body> </Envelope>' \ http://localhost:8082/calculator-ws/CalculatorService <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:addResponse xmlns:ns2="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator"> <return>11</return> 2 </ns2:addResponse> </soap:Body> </soap:Envelope> 1 The request to add 7 and 4 2 11 - return value of the operation 6.4.2. SOAP client Now let's have a look how we can get the client inside a Quarkus application. First, we need the Service Endpoint Interface (SEI) and all other model classes it requires. There are several ways to get them: Write by hand Copy from the Web Sevice project, if it is written in Java Have a Maven artifact containing the model classes, perhaps it is offered by the Service project Generate the model classes from WSDL The last option tends to be the easiest and most flexible for client applications. Tip If you want to use this approach, first follow the Generate Java from WSDL section and then continue with the steps. 6.4.3. Using SEI as a client In our case, the Service Endpoint Interface (SEI) is org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService . As usual on Quarkus, we can obtain an instance of it via CDI. To make it testable easily, we'll wrap it in a REST service: CxfClientResource.java package io.quarkiverse.cxf.client.it; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService; import io.quarkiverse.cxf.annotation.CXFClient; @Path("/cxf/calculator-client") public class CxfClientRestResource { @CXFClient("myCalculator") 1 CalculatorService myCalculator; @GET @Path("/add") @Produces(MediaType.TEXT_PLAIN) public int add(@QueryParam("a") int a, @QueryParam("b") int b) { return myCalculator.add(a, b); 2 } } 1 Let the CDI container inject an instance of the client. @CXFClient("myCalculator") is actually equivalent to @Inject @CXFClient("myCalculator") 2 Invoke the add operation thus calling the remote Web service Is this all we need for the client to work? - No, in addition to the above, we need to tell a few other things to the CXF Quarkus extension in application.properties : application.properties cxf.it.calculator.baseUri=http://localhost:8082 quarkus.cxf.client.myCalculator.wsdl = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.myCalculator.client-endpoint-url = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService quarkus.cxf.client.myCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService Tip All client configuration properties are documented in the Configuration properties reference. With all the above files in place, we should be able to start the application in Quarkus dev mode USD mvn quarkus:dev ... INFO [io.quarkus] (Quarkus Main Thread) ... Listening on: http://localhost:8080 and test it by sending some requests to it: USD curl -s 'http://localhost:8080/cxf/calculator-client/add?a=5&b=6' 11 where 11 is the correct result of adding 5 and 6 . 6.4.4. Further steps You may want to proceed with Package for JVM and native . Advanced SOAP client topics 6.5. Configuration Quarkus CXF exposes a large number of configuration options. Each extension documents its options at the bottom of its reference page . The configuration options can be set in application.properties file or via environment variables - see Quarkus configuration reference for details. 6.5.1. Bean references Several configuration options of Quarkus CXF allow referring to beans present in Quarkus CDI container. Features and interceptors are typical examples of those. There are two ways how to set a bean reference in the configuration: by type or by bean name. 6.5.1.1. Bean reference by type Here is an example: application.properties # bean reference by type quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature When using a reference by type name, the resolution proceeds as follows: Fist the bean is looked up in Quarkus CDI container by type. If the bean is available, it is used. If multiple beans assignable to the given type, then an exception is thrown. If no matching bean is available, then the class is loaded and an attempt is performed to instantiate it using its default constructor. 6.5.1.2. Bean reference by bean name Here is an example: application.properties # bean reference by bean name quarkus.cxf.endpoint."/fruit".features = #myCustomLoggingFeature When using a reference by bean name, then unsurprisingly, the bean is looked up in Quarkus CDI container by name. A named bean called myCustomLoggingFeature can be defined as follows: import org.apache.cxf.ext.logging.LoggingFeature; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; class Producers { @Produces @ApplicationScoped @Named("myCustomLoggingFeature") LoggingFeature myCustomLoggingFeature() { LoggingFeature loggingFeature = new LoggingFeature(); loggingFeature.setPrettyLogging(true); return loggingFeature; } } 6.6. Package for running on a JVM or natively In this chapter, we explain how to package a Quarkus CXF application for running on a JVM or for running it natively. 6.6.1. JVM mode In the introductory guides for SOAP client and SOAP service, we worked only in Quarkus dev mode: Quarkus tooling was running on the background, watching for changes in our workspace, recompiling and reloading the application as needed. How do we run the application on a JVM once we are done with the development? First we need to package it with Maven: USD mvn package The libraries needed to run the application on a JVM can be found in target/quarkus-app directory: USD ls -lh target/quarkus-app drwxr-xr-x. 2 ppalaga ppalaga 4.0K Jan 12 22:29 app drwxr-xr-x. 4 ppalaga ppalaga 4.0K Jan 12 22:29 lib drwxr-xr-x. 2 ppalaga ppalaga 4.0K Jan 12 22:29 quarkus -rw-r-r--. 1 ppalaga ppalaga 6.1K Jan 12 22:29 quarkus-app-dependencies.txt -rw-r-r--. 1 ppalaga ppalaga 678 Jan 12 22:29 quarkus-run.jar We can start the application as follows: USD java -jar target/quarkus-app/quarkus-run.jar You can send some SOAP requests using curl to make sure that the application works. 6.6.2. Native mode Quarkus offers first class support for building GraalVM native images and Quarkus CXF fully honors that promise too. Images GraalVM native images are platform specific executable files that you can run directly without a JVM. They boot faster and spend less memory compared to running the same application in JVM mode. The pom.xml file generated by code.quarkus.redhat.com contains the native profile needed for building the native image: <profile> <id>native</id> <activation> <property> <name>native</name> </property> </activation> <properties> <skipITs>false</skipITs> <quarkus.package.type>native</quarkus.package.type> </properties> </profile> Further, as mentioned in the Section 6.2.2, "Create project" section, you need the GraalVM native-image tool. You should either have it installed locally and have GRAALVM_HOME environment variable set properly, or - if you only need to produce a Linux native executable - you can use docker . With local installation of GraalVM # Make sure USDGRAALVM_HOME is set properly USD echo USDGRAALVM_HOME /home/{user}/.sdkman/candidates/java/{major}.{minor}.r{java-version}-grl # Produce the native executable mvn package -Pnative Tip Quarkus is quite picky about the GraalVM version. When using the local installation, always make sure that you use the version preferred by Quarkus. You can do that by opening quarkus-bom imported in your pom.xml and searching for graalvm there. If you use Docker, Quarkus takes care for pulling the right version for you. With docker # Produce the native executable mvn package -Pnative -Dquarkus.native.container-build=true This can take a minute or so for a simple application. When the build is done, the native executable should be available in target directory: USD ls -l target ... -rwxr-xr-x. 1 ppalaga ppalaga 71M Jan 11 22:42 quarkus-cxf-integration-test-server-1.8.0-SNAPSHOT-runner ... As you can see, it has a size of only 71 MB, and is executable. You can run it as follows: USD target/*-runner ... INFO [io.quarkus] (main) quarkus-cxf-integration-test-server 1.8.0-SNAPSHOT native (powered by Quarkus 2.15.2.Final) started in 0.042s. Listening on: http://0.0.0.0:8080 ... Again, you can send some SOAP requests using curl to make sure that the native executable works. Do not forget to compare the memory usage, time to first request and other performance metrics with the stack you used before and share your results! 6.6.3. Native Image: Additional Resources You may also refer to the links below which contain tips on how to work with native images. Quarkus: Building a Native Executable Quarkus: Tips for Writing Native Applications Quarkus: Native Reference Guide GraalVM: Accessing Resources in Native Images GraalVM: Reflection Use in Native Images GraalVM: Tracing Agent 6.6.4. Create container image Refer to Quarkus Container image guide. 6.7. Logging Refer to Quarkus Logging guide for basic information about logging on Quarkus, such as Getting a logger in your application code Log levels Categories Format JSON format 6.7.1. Payload logging History Since Quarkus CXF 2.6.0, the payload logging functionality is available via io.quarkiverse.cxf:quarkus-cxf extension. Before 2.6.0, it was available through a separate extension io.quarkiverse.cxf:quarkus-cxf-rt-features-logging which is now deprecated and will be removed in the future. The payload logging functionality is implemented primarily through the org.apache.cxf.ext.logging.LoggingFeature class. There are several ways how you can set the feature on a client or service endpoint. 6.7.2. Configuring payload logging through configuration properties 6.7.2.1. Global settings The global logging options exist since Quarkus CXF 2.6.0. They need to be enabled using quarkus.cxf.logging.enabled-for . There are four possible values: none (default) - the global logging feature is enabled for neither clients nor service endpoints clients - the global logging feature is enabled for all clients in the application services - the global logging feature is enabled for all service endpoints in the application both - the global logging feature is enabled for all clients and service endpoints in the application The global settings can be overriden on the client or service endpoint level. application.properties # Global settings quarkus.cxf.logging.enabled-for = both quarkus.cxf.logging.pretty = true All logging configuration options are listed on quarkus-cxf reference page. Tip All logging properties mentioned on this page are runtime configuration options. Hence you can pass them when starting the application without having to rebuild it. It can be done either by passing a system property on the command line (e.g. -Dquarkus.cxf.logging.enabled-for=both ) or by setting an environment variable (e.g. export QUARKUS_CXF_LOGGING_ENABLED_FOR=both ). 6.7.2.2. Per client and per service endpoint settings Since Quarkus CXF 2.5.0, the LoggingFeature can be configured and attached to a client or a service endpoint declaratively by setting the appropriate options in application.properties : application.properties # For a service: quarkus.cxf.endpoint."/hello".logging.enabled = true quarkus.cxf.endpoint."/hello".logging.pretty = true # For a client: quarkus.cxf.client.hello.logging.enabled = true quarkus.cxf.client.hello.logging.pretty = true All logging configuration options are documented on quarkus-cxf reference page: For clients For service endpoints 6.7.3. Alternative ways of adding a LoggingFeature to a client or service To attach an instance with default settings, you can do one of the following: In application.properties : # For a service: quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature # For a client: quarkus.cxf.client."myClient".features = org.apache.cxf.ext.logging.LoggingFeature Tip There is an example in Your first SOAP Web service chapter of the User guide. or alternatively Use the @Features annotation of CXF: @org.apache.cxf.feature.Features (features = {"org.apache.cxf.ext.logging.LoggingFeature"}) @WebService(endpointInterface = "org.acme.SayHi", targetNamespace = "uri:org.acme") public class SayHiImplementation implements SayHi { public long sayHi(long arg) { return arg; } //... } 6.7.3.1. Producing a custom LoggingFeature bean If you need some custom logic to setup the LoggingFeature , you can produce a named LoggingFeature bean: import org.apache.cxf.ext.logging.LoggingFeature; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; class Producers { @Produces @ApplicationScoped @Named("limitedLoggingFeature") // "limitedLoggingFeature" is redundant if the name of the method is the same LoggingFeature limitedLoggingFeature() { LoggingFeature loggingFeature = new LoggingFeature(); loggingFeature.setPrettyLogging(true); loggingFeature.setLimit(1024); return loggingFeature; } } Then you can refer to it by its name prefixed with # in application.properties : # For a service: quarkus.cxf.endpoint."/hello".features = #limitedLoggingFeature # For a client: quarkus.cxf.client.hello.features = #limitedLoggingFeature 6.8. Complex SOAP payloads with JAXB Our introductory guides for Quarkus ug_first-soap-client[SOAP client] and SOAP service dealt with services having only primitive parameters and return values such as integers and strings. Let's have a look at passing and receiving more complex objects. As an example, let's create an application for managing fruits. Note The sample code snippets used in this section come from the server integration test in the source tree of Quarkus CXF Because our representation of fruit is supposed to be a complex, let's model it as a Java bean with a couple of attributes: package io.quarkiverse.cxf.it.server; import java.util.Objects; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @XmlType(name = "Fruit") @XmlRootElement public class Fruit { private String name; private String description; public Fruit() { } public Fruit(String name, String description) { this.name = name; this.description = description; } public String getName() { return name; } @XmlElement public void setName(String name) { this.name = name; } public String getDescription() { return description; } @XmlElement public void setDescription(String description) { this.description = description; } @Override public boolean equals(Object obj) { if (!(obj instanceof Fruit)) { return false; } Fruit other = (Fruit) obj; return Objects.equals(other.getName(), this.getName()); } @Override public int hashCode() { return Objects.hash(this.getName()); } } As you may have noticed, we have used some JAXB annotations, such as @XmlElement , @XmlRootElement and @XmlType . This is to control the serialization and deserialization of the bean from and to XML. 6.8.1. Automatic registration for reflection JAXB is a reflection based serialization framework. When learning about GraalVM native images, one of the first things you typically hear is that you have to register classes, fields and methods for reflection at build time. With plain GraalVM you'd have to do this through reflection-config.json manually. Well, at least for the classes you have written yourself. Not so with Quarkus. quarkus-jaxb extension (which quarkus-cxf depends on) is able to scan your application's class path for classes annotated with JAXB annotations and register them for reflection automatically. Hence working with complex payloads on Quarkus is not different from stock CXF. The JAXB serialization and deserialization will work out of the box without any additional configuration. 6.8.2. SEI and implementation The Service Endpoint Interface (SEI) for managing fruits might look like the following: package io.quarkiverse.cxf.it.server; import java.util.Set; import jakarta.jws.WebMethod; import jakarta.jws.WebService; @WebService public interface FruitService { @WebMethod Set<Fruit> list(); @WebMethod Set<Fruit> add(Fruit fruit); @WebMethod Set<Fruit> delete(Fruit fruit); } We can implement the SEI as simply as possible: package io.quarkiverse.cxf.it.server; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import jakarta.jws.WebService; @WebService(serviceName = "FruitService") public class FruitServiceImpl implements FruitService { private Set<Fruit> fruits = Collections.synchronizedSet(new LinkedHashSet<>()); public FruitServiceImpl() { fruits.add(new Fruit("Apple", "Winter fruit")); fruits.add(new Fruit("Pineapple", "Tropical fruit")); } @Override public Set<Fruit> list() { return fruits; } @Override public Set<Fruit> add(Fruit fruit) { fruits.add(fruit); return fruits; } @Override public Set<Fruit> delete(Fruit fruit) { fruits.remove(fruit); return fruits; } } 6.8.3. application.properties The implementation is pretty straightforward and you just need to define your endpoints using the application.properties . quarkus.cxf.endpoint."/fruits".implementor = io.quarkiverse.cxf.it.server.FruitServiceImpl quarkus.cxf.endpoint."/fruits".features = org.apache.cxf.ext.logging.LoggingFeature 6.8.4. Test with Quarkus dev mode and curl Having the above files in place, you can start Quarkus tooling in dev mode : USD mvn quarkus:dev ... INFO [io.quarkus] (Quarkus Main Thread) ... Listening on: http://localhost:8080 and then check whether the service is working by invoking its list operation: USD curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" \ -d \ '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <soapenv:Body> <ns1:list/> </soapenv:Body> </soapenv:Envelope>' \ http://localhost:8080/soap/fruits ... <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns1:listResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Winter fruit</description> <name>Apple</name> </return> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Tropical fruit</description> <name>Pineapple</name> </return> </ns1:listResponse> </soap:Body> </soap:Envelope> As you can see, the endpoint has returned the two fruits Apple and Pineapple available by default. Now let's add another fruit, say an Orange : USD curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" \ -d \ '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:add xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <arg0> <description>Mediterranean fruit</description> <name>Orange</name> </arg0> </ns2:add> </soap:Body></soap:Envelope>' \ http://localhost:8080/soap/fruits ... <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns1:addResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Winter fruit</description> <name>Apple</name> </return> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Tropical fruit</description> <name>Pineapple</name> </return> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Mediterranean fruit</description> <name>Orange</name> </return> </ns1:addResponse> </soap:Body> </soap:Envelope> We can see Orange having been added in the returned list as expected. 6.8.5. Further steps You may want to proceed with packaging your application for running on a JVM or natively . 6.9. SSL, TLS and HTTPS This chapter documents various use cases related to SSL, TLS and HTTPS. Note The sample code snippets used in this section come from the WS-SecurityPolicy integration test in the source tree of Quarkus CXF 6.9.1. Client SSL configuration If your client is going to communicate with a server whose SSL certificate is not trusted by the client's operating system, then you need to set up a custom trust store for your client. Tools like openssl or Java keytool are commonly used for for creating and maintaining truststores. We have examples for both tools in Quarkus CXF source tree: {link-quarkus-cxf-source-tree-base}/integration-tests/ws-security-policy/pom.xml#L127-L440[Create truststore with Java 'keytool' (wrapped by a Maven plugin)] {link-quarkus-cxf-source-tree-base}/integration-tests/ws-security-policy/generate-certs.sh[Create truststore with openssl ] Once you have prepared the trust store, you need to configure your client to use it. 6.9.1.1. Set the client trust store in application.properties This is the easiest way to set the client trust store. The key role is played by the following properties: quarkus.cxf.client."client-name".trust-store quarkus.cxf.client."client-name".trust-store-type quarkus.cxf.client."client-name".trust-store-password Here is an example: application.properties keystore.type = jks 1 quarkus.cxf.client.hello.client-endpoint-url = https://localhost:USD{quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.hello.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.hello.trust-store-type = USD{keystore.type} 2 quarkus.cxf.client.hello.trust-store = client-truststore.USD{keystore.type} quarkus.cxf.client.hello.trust-store-password = client-truststore-password 1 pkcs12 trust store type is a common alternative to jks . 2 The referenced client-truststore.jks file has to be available in src/main/resources directory. 6.9.2. Server SSL configuration To make your services available over the HTTPS protocol, you need to setup server keystore in the first place. The server SSL configuration is driven by Vert.x, the HTTP layer of Quarkus. Quarkus HTTP guide provides the information about the configuration options. Here is a basic example: application.properties 1 quarkus.http.ssl.certificate.key-store-file = localhost-keystore.USD{keystore.type} quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password quarkus.http.ssl.certificate.key-store-key-alias = localhost quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password 1 The referenced localhost.jks file has to be available in src/main/resources directory. 6.9.3. Mutual TLS (mTLS) authentication So far, we have explained the simple or single-sided case where only the server proves its identity through an SSL certificate and the client has to be set up to trust that certificate. Mutual TLS authentication goes by letting also the client prove its identity using the same means of public key cryptography. Hence, for the Mutual TLS (mTLS) authentication, in addition to setting up the server keystore and client truststore as described above, you need to setup the keystore on the client side and the truststore on the server side. The tools for creating and maintaining the stores are the same and the configuration properties to use are pretty much analogous to the ones used in the Simple TLS case. The mTLS integration test in the Quarkus CXF source tree can serve as a good starting point. The keystores and truststores are created with {link-quarkus-cxf-source-tree-base}/integration-tests/mtls/generate-certs.sh[openssl] (or alternatively with Java {link-quarkus-cxf-source-tree-base}/integration-tests/mtls/pom.xml#L109-L377[Java keytool ]) Here is the application.properties file: application.properties # The store type could also be jks keystore.type = pkcs12 # Server keystore for Simple TLS quarkus.http.ssl.certificate.key-store-file = localhost-keystore.USD{keystore.type} quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password quarkus.http.ssl.certificate.key-store-key-alias = localhost quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password # Server truststore for Mutual TLS quarkus.http.ssl.certificate.trust-store-file = localhost-truststore.USD{keystore.type} quarkus.http.ssl.certificate.trust-store-password = localhost-truststore-password # Do not allow any clients which do not prove their indentity through an SSL certificate quarkus.http.ssl.client-auth = required # CXF service quarkus.cxf.endpoint."/mTls".implementor = io.quarkiverse.cxf.it.auth.mtls.MTlsHelloServiceImpl # CXF client with a properly set certificate for mTLS quarkus.cxf.client.mTls.client-endpoint-url = https://localhost:USD{quarkus.http.test-ssl-port}/services/mTls quarkus.cxf.client.mTls.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.mTls.key-store = target/classes/client-keystore.USD{keystore.type} quarkus.cxf.client.mTls.key-store-type = USD{keystore.type} quarkus.cxf.client.mTls.key-store-password = client-keystore-password quarkus.cxf.client.mTls.key-password = client-keystore-password quarkus.cxf.client.mTls.trust-store = target/classes/client-truststore.USD{keystore.type} quarkus.cxf.client.mTls.trust-store-type = USD{keystore.type} quarkus.cxf.client.mTls.trust-store-password = client-truststore-password # Include the keystores in the native executable quarkus.native.resources.includes = *.pkcs12,*.jks 6.9.4. Enforce SSL through WS-SecurityPolicy The requirement for the clients to connect through HTTPS can be defined in a policy. The functionality is provided by quarkus-cxf-rt-ws-security extension. Here is an example of a policy file: https-policy.xml <?xml version="1.0" encoding="UTF-8"?> <wsp:Policy wsp:Id="HttpsSecurityServicePolicy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding> <wsp:Policy> <sp:TransportToken> <wsp:Policy> <sp:HttpsToken RequireClientCertificate="false" /> </wsp:Policy> </sp:TransportToken> <sp:IncludeTimestamp /> <sp:AlgorithmSuite> <wsp:Policy> <sp:Basic128 /> </wsp:Policy> </sp:AlgorithmSuite> </wsp:Policy> </sp:TransportBinding> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> The policy has to be referenced from a service endpoint interface (SEI): HttpsPolicyHelloService.java package io.quarkiverse.cxf.it.security.policy; import jakarta.jws.WebMethod; import jakarta.jws.WebService; import org.apache.cxf.annotations.Policy; /** * A service implementation with a transport policy set */ @WebService(serviceName = "HttpsPolicyHelloService") @Policy(placement = Policy.Placement.BINDING, uri = "https-policy.xml") public interface HttpsPolicyHelloService extends AbstractHelloService { @WebMethod @Override public String hello(String text); } With this setup in place, any request delivered over HTTP will be rejected by the PolicyVerificationInInterceptor : ERROR [org.apa.cxf.ws.pol.PolicyVerificationInInterceptor] Inbound policy verification failed: These policy alternatives can not be satisfied: {http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}TransportBinding: TLS is not enabled ... 6.10. Authentication and authorization Note The sample code snippets shown in this section come from the Client and server integration test in the source tree of Quarkus CXF. You may want to use it as a runnable example. 6.10.1. Client HTTP basic authentication Use the following client configuration options provided by quarkus-cxf extension to pass the username and password for HTTP basic authentication: quarkus.cxf.client."client-name".username quarkus.cxf.client."client-name".password Here is an example: application.properties quarkus.cxf.client.basicAuth.wsdl = http://localhost:USD{quarkus.http.test-port}/soap/basicAuth?wsdl quarkus.cxf.client.basicAuth.client-endpoint-url = http://localhost:USD{quarkus.http.test-port}/soap/basicAuth quarkus.cxf.client.basicAuth.username = bob quarkus.cxf.client.basicAuth.password = bob234 6.10.1.1. Accessing WSDL protected by basic authentication By default, the clients created by Quarkus CXF do not send the Authorization header, unless you set the quarkus.cxf.client."client-name".secure-wsdl-access to true : application.properties quarkus.cxf.client.basicAuthSecureWsdl.wsdl = http://localhost:USD{quarkus.http.test-port}/soap/basicAuth?wsdl quarkus.cxf.client.basicAuthSecureWsdl.client-endpoint-url = http://localhost:USD{quarkus.http.test-port}/soap/basicAuthSecureWsdl quarkus.cxf.client.basicAuthSecureWsdl.username = bob quarkus.cxf.client.basicAuthSecureWsdl.password = USD{client-server.bob.password} quarkus.cxf.client.basicAuthSecureWsdl.secure-wsdl-access = true 6.10.2. Mutual TLS (mTLS) authentication See the Mutual TLS (mTLS) authentication section in SSL, TLS and HTTPS guide. 6.10.3. Securing service endpoints The server-side authentication and authorization is driven by Quarkus Security , especially when it comes to Authentication mechanisms Identity providers Role-based access control (RBAC) There is a basic example in our Client and server integration test . Its key parts are: io.quarkus:quarkus-elytron-security-properties-file dependency as an Identity provider Basic authentication enabled and users with their roles configured in application.properties : application.properties quarkus.http.auth.basic = true quarkus.security.users.embedded.enabled = true quarkus.security.users.embedded.plain-text = true quarkus.security.users.embedded.users.alice = alice123 quarkus.security.users.embedded.roles.alice = admin quarkus.security.users.embedded.users.bob = bob234 quarkus.security.users.embedded.roles.bob = app-user Role-based access control enfoced via @RolesAllowed annotation: BasicAuthHelloServiceImpl.java package io.quarkiverse.cxf.it.auth.basic; import jakarta.annotation.security.RolesAllowed; import jakarta.jws.WebService; import io.quarkiverse.cxf.it.HelloService; @WebService(serviceName = "HelloService", targetNamespace = HelloService.NS) @RolesAllowed("app-user") public class BasicAuthHelloServiceImpl implements HelloService { @Override public String hello(String person) { return "Hello " + person + "!"; } } 6.11. Advanced SOAP client topics 6.11.1. client-endpoint-url defaults If you omit the client-endpoint-url property in application.properties , the CXF Quarkus extension will assume that the service is published at http://localhost:8080/{service-path} , where {service-path} is derived from Configuration property quarkus.cxf.path (if specified); and the SEI's class name in lower case Given quarkus.cxf.path = /ws , the default effective client-endpoint-url of the CalculatorService would be http://localhost:8080/ws/org.jboss.eap.quickstarts.wscalculator.calculator.calculatorservice . If quarkus.cxf.path is not specified, the client-endpoint-url would be just http://localhost:8080/org.jboss.eap.quickstarts.wscalculator.calculator.calculatorservice . 6.11.2. Configure multiple clients In the example above, we configured just a single client called myCalculator . Of course, you can configure multiple clients pointing at different URLs and/or implementing different SEIs using multiple identifiers: application.properties cxf.it.calculator.baseUri = http://localhost:8082 quarkus.cxf.client.myCalculator.wsdl = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.myCalculator.client-endpoint-url = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService quarkus.cxf.client.myCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService # another client quarkus.cxf.client.anotherCalculator.wsdl = https://acme.com/ws/WeatherService?wsdl quarkus.cxf.client.anotherCalculator.client-endpoint-url = https://acme.com/ws/WeatherService quarkus.cxf.client.anotherCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService 6.11.3. Advanced Client Configurations To globally configure all clients in your application, you can use the example snippet below to configure the HttpConduit . This allows you to set the HTTPClientPolicy , AuthorizationPolicy , ProxyAuthorizationPolicy or even TLSClientParameters for your clients. void onStart(@Observes StartupEvent ev) { HTTPConduitConfigurer httpConduitConfigurer = new HTTPConduitConfigurer() { public void configure(String name, String address, HTTPConduit c) { AsyncHTTPConduit conduit = (AsyncHTTPConduit)c; // use setter to configure client conduit.getHttpAsyncClient().getCredentialsProvider().setCredentials( AuthScope.ANY, new NTCredentials( USER,PWD, "", DOM ) ); conduit.getClient().setAllowChunking( false ); conduit.getClient().setAutoRedirect( true ); } }; final Bus bus = BusFactory.getDefaultBus(); bus.setExtension(httpConduitConfigurer, HTTPConduitConfigurer.class); } 6.11.4. Pure client applications Quarkus batch (e.g. periodically scheduled), or command line applications, may do without an HTTP server. Use the property below to prevent launching the HTTP server at startup: quarkus.http.host-enabled = false 6.11.5. Prevent resource leaks CXF client proxies implement java.io.Closeable . Therefore, it is important to call ((Closeable) proxy).close() once the client is not needed anymore to free all associated system resources, such as threads. Quarkus CXF takes care for closing the clients injected via @io.quarkiverse.cxf.annotation.CXFClient automatically as soon as they are disposed by the CDI container. For client proxies created manually, it is up to you to call ((Closeable) proxy).close() : import java.net.URL; import javax.xml.namespace.QName; import jakarta.xml.ws.Service; import java.io.Closeable; final URL serviceUrl = new URL("http://localhost/myService?wsdl"); final QName qName = new QName("http://acme.org/myNamespace", "MyService"); final Service service = jakarta.xml.ws.Service.create(serviceUrl, qName); final MyService proxy = service.getPort(MyService.class); try { proxy.doSomething(); } finally { ((Closeable) proxy).close(); } 6.12. Running behind a reverse proxy SOAP requests aimed towards services running on Quarkus can be routed through proxies that generate additional headers (e.g. X-Forwarded-Host ) to keep information from the client-facing side of the proxy servers that is altered or lost when they are involved. In those scenarios, Quarkus can be configured to automatically update information like protocol, host, port and URI reflecting the values in these headers. Tip Refer to Quarkus HTTP reference for more details. Quarkus CXF support for various X-Forwarded headers works in line with Quarkus configuration. Important Activating this feature leaves the server exposed to several security issues (i.e. information spoofing). Consider activating it only when running behind a reverse proxy. These are the relevant Quarkus properties and their effect on Quarkus CXF: quarkus.http.proxy.proxy-address-forwarding - the main switch to enable the rewriting of the request destination parts. If enabled, the rewriting of the request fields will be effective throughout the whole CXF server stack. If enabled, the values passed via X-Forwarded-Proto and X-Forwarded-Port headers will be used to set the protocol part and the port part of the URL returned by jakarta.servlet.http.HttpServletRequest.getRequestURL() respectively. If enabled, the value passed via X-Forwarded-For will be returned by jakarta.servlet.ServletRequest.getRemoteAddr() . quarkus.http.proxy.enable-forwarded-host - enable the rewriting of the host part of URL returned by jakarta.servlet.http.HttpServletRequest.getRequestURL() . The actual host name is taken from the header configured via quarkus.http.proxy.forwarded-host-header (default is X-Forwarded-Host ). quarkus.http.proxy.enable-forwarded-prefix - enable the rewriting of the path part of the URL returned by jakarta.servlet.http.HttpServletRequest.getRequestURL() and of the URI returned by jakarta.servlet.http.HttpServletRequest.getRequestURI() . The actual path prefix is taken from the header configured via quarkus.http.proxy.forwarded-prefix-header (default is X-Forwarded-Prefix ). Here is the most common snippet to copy to your application.properties : quarkus.http.proxy.proxy-address-forwarding = true quarkus.http.proxy.enable-forwarded-host = true quarkus.http.proxy.enable-forwarded-prefix = true One of the observable effects of these settings is the change of the location value in WSDL served on http://localhost:8080/services/my-service?wsdl . For example, if the request contains the following headers then the WSDL served on http://localhost:8080/services/my-service?wsdl would contain the following location : ... <soap:address location="https://api.example.com:443/my-prefix/services/my-service"/> ... 6.13. Contract first and code first approaches Both contract first and code first development modes are fully supported by Quarkus CXF. 6.13.1. Contract first client A SOAP service is described by WSDL. It is a contract defining operations, their parameters and return values, etc. WSDL is rich enough to be used for generating the code of a complete client. CXF provides the wsdl2java utility for that. Quarkus CXF wraps wsdl2java in the quarkus-cxf extension so you do not need to use it directly. Follow the Generate the Model classes from WSDL section of the user guide for more details about how to use it. You may also want to check the CXF Developing a Consumer as a general introduction. 6.13.2. Contract first service When implementing a service the generation of Java code from WSDL may also come in handy. wsdl2java can generate the model classes (with JAXB annotations) and service interfaces (with JAX-WS annotations) for you. Your task is then to provide implementations for those interfaces. You may want to check the WSDL First Service Development section of CXF documentation for a better understanding of the underlying concepts. 6.13.3. Code first service Another valid option at your disposal is to write your service in Java, using JAX-WS and JAXB. Then you have two options how to obtain the WSDL contract: Start your service and point your clients at http://your-host/your-service?wsdl Generate the WSDL document from Java classes at build time Tip Check the Code first development section of CXF documentation for further information. 6.14. Generate the Model classes from WSDL quarkus-cxf extension supports generating Java classes from WSDL during Quarkus code generation phase. Code examples The code snippets shown in this section come from the client integration test in the source tree of Quarkus CXF. You may want to check it as an executable example. You need to set up a couple of things for CXF code generation to work: Have io.quarkiverse.cxf:quarkus-cxf dependency in your project For Maven projects, the generate-code goal needs to be present in the configuration of quarkus-maven-plugin : pom.xml <plugin> <groupId>io.quarkus</groupId> <artifactId>quarkus-maven-plugin</artifactId> <executions> <execution> <goals> <goal>build</goal> <goal>generate-code</goal> </goals> </execution> </executions> </plugin> For Gradle projects no additional configurarion of io.quarkus plugin is needed Put your WSDL files under src/main/resources or src/test/resources or any subdirectory thereof. Your WSDL file names must end with .wsdl Set quarkus.cxf.codegen.wsdl2java.includes configuration property to a pattern matching the WSDL files you wish to process. If you want to process all WSDL files under src/main/resources/wsdl or src/test/resources/wsdl , set it as follows: application.properties quarkus.cxf.codegen.wsdl2java.includes = wsdl/*.wsdl This will generate Java classes in target/generated-sources/wsdl2java or target/generated-test-sources/wsdl2java directory. They will be automatically picked by the compiler plugin there. Hence we are free to refer to them from our application or test code. Note that quarkus-cxf code generation uses the wsdl2Java utility from CXF under the hood. wsdl2Java is called separately for each WSDL file selected by includes and excludes . Passing custom parameters to wsdl2java is possible through quarkus.cxf.codegen.wsdl2java.additional-params configuration parameter. If you need different additional-params for each WSDL file, you may want to define a separate named parameter set for each one of them. Here is an example: application.properties # Parameters for foo.wsdl quarkus.cxf.codegen.wsdl2java.foo-params.includes = wsdl/foo.wsdl quarkus.cxf.codegen.wsdl2java.foo-params.wsdl-location = wsdl/foo.wsdl # Parameters for bar.wsdl quarkus.cxf.codegen.wsdl2java.bar-params.includes = wsdl/bar.wsdl quarkus.cxf.codegen.wsdl2java.bar-params.wsdl-location = wsdl/bar.wsdl quarkus.cxf.codegen.wsdl2java.bar-params.xjc = ts Tip Add io.quarkiverse.cxf:quarkus-cxf-xjc-plugins dependency to your project to be able to use -xjc-Xbg , -xjc-Xdv , -xjc-Xjavadoc , -xjc-Xproperty-listener , -xjc-Xts and -xjc-Xwsdlextension wsdl2java parameters. 6.15. Generate WSDL document from Java If the WSDL served by your service at http://your-host/your-service?wsdl is not enough because you e.g. want to distribute it as a Maven artifact, then you can use java2ws to generate the WSDL document at build time. You do not need to invoke the java2ws tool provided by CXF directly, neither you have to use the cxf-java2ws-plugin . quarkus-cxf wraps java2ws and so you can configure it in application.properties as any other aspect of your application. Here is an example: Note The sample code snippets used in this section come from the server integration test in the source tree of Quarkus CXF application.properties quarkus.cxf.java2ws.includes = io.quarkiverse.cxf.it.server.HelloService,io.quarkiverse.cxf.it.server.FaultyHelloService quarkus.cxf.java2ws.wsdl-name-template = %TARGET_DIR%/Java2wsTest/%SIMPLE_CLASS_NAME%-from-java2ws.wsdl Here we have instructed java2ws to generate WSDLs for two interfaces, namely HelloService and FaultyHelloService . Annotations Note that the Service interfaces must be annotated with jakarta.xml.ws.WebService to be selected for java2ws processing. The two generated WSDL documents will be stored as target/Java2wsTest/FaultyHelloService-from-java2ws.wsdl and target/Java2wsTest/HelloService-from-java2ws.wsdl respectively. Note Unlike wsdl2java which is executed within Quarkus source generation phase, java2ws is a part Quarkus augmentation that happens after compilation. The input of java2ws are, after all, Java class files. Hence you do not need to add <goal>generate-code</goal> to quarkus-maven-plugin for java2ws . 6.15.1. See also quarkus.cxf.java2ws.* configuration properties of quarkus-cxf 6.16. CXF Interceptors and Features CXF interceptors and CXF features can be added to both your client or server using either annotations or application.properties configurations. While CXF provides a number of out of the box embedded interceptors and features, you can also integrate your custom developed implementations. Annotations can be used on either the service interface or implementor classes. @org.apache.cxf.feature.Features (features = {"org.apache.cxf.ext.logging.LoggingFeature"}) @org.apache.cxf.interceptor.InInterceptors (interceptors = {"org.acme.Test1Interceptor" }) @org.apache.cxf.interceptor.InFaultInterceptors (interceptors = {"org.acme.Test2Interceptor" }) @org.apache.cxf.interceptor.OutInterceptors (interceptors = {"org.acme.Test1Interceptor" }) @org.apache.cxf.interceptor.InFaultInterceptors (interceptors = {"org.acme.Test2Interceptor","org.acme.Test3Intercetpor" }) @WebService(endpointInterface = "org.acme.SayHi", targetNamespace = "uri:org.acme") public class SayHiImplementation implements SayHi { public long sayHi(long arg) { return arg; } //... } You may also define your configurations in the application.properties file. quarkus.cxf.endpoint."/greeting-service".features=org.apache.cxf.ext.logging.LoggingFeature quarkus.cxf.endpoint."/greeting-service".in-interceptors=org.acme.Test1Interceptor quarkus.cxf.endpoint."/greeting-service".out-interceptors=org.acme.Test1Interceptor quarkus.cxf.endpoint."/greeting-service".in-fault-interceptors=org.acme.Test2Interceptor,org.acme.Test3Intercetpor quarkus.cxf.endpoint."/greeting-service".out-fault-interceptors=org.acme.Test1Intercetpor Class loading Both feature and interceptor classes are loaded via CDI first. They can be referenced by fully a qualified class name or by a bean name . If no CDI beans are available, the constructor without parameters will be invoked to instantiate each class. 6.17. JAX-WS Handlers As an alternative to the @HandlerChain annotation, JAX-WS Handlers can be added to both your client or server via application.properties : application.properties # A web service endpoint with multiple Handler classes quarkus.cxf.endpoint."/greeting-service".handlers=org.acme.MySOAPHandler,org.acme.AnotherSOAPHandler # A web service client with a single Handler quarkus.cxf.client."greeting-client".handlers=org.acme.MySOAPHandler Where MySOAPHandler could look like below: import jakarta.xml.ws.handler.soap.SOAPHandler; import jakarta.xml.ws.handler.soap.SOAPMessageContext; public class MySOAPHandler implements SOAPHandler<SOAPMessageContext> { public boolean handleMessage(SOAPMessageContext messageContext) { SOAPMessage msg = messageContext.getMessage(); return true; } // other methods } Class loading The SOAPHandler classes are loaded via CDI first.. If no CDI beans are available, the constructor without parameters will be invoked to instantiate each class. 6.18. JAX-WS Providers JAX-WS Providers are fully supported, and can be implemented as shown below. Given the following sample Provider implementation: import jakarta.xml.transform.stream.StreamSource; import jakarta.xml.ws.BindingType; import jakarta.xml.ws.Provider; import jakarta.xml.ws.Service; import jakarta.xml.ws.ServiceMode; import jakarta.xml.ws.WebServiceProvider; import java.io.StringReader; @WebServiceProvider @ServiceMode(value = Service.Mode.PAYLOAD) public class StreamSourcePayloadProvider implements Provider<StreamSource> { public StreamSourcePayloadProvider() { } public StreamSource invoke(StreamSource request) { String payload = StaxUtils.toString(request); // Do some interesting things ... StreamSource response = new StreamSource(new StringReader(payload)); return response; } } The application.properties can be configured as shown below. # A web service endpoint with the Provider implementation class quarkus.cxf.endpoint."/stream-source".implementor=org.acme.StreamSourcePayloadProvider Class loading Provider classes are loaded via CDI first.. If no CDI beans are available, the constructor without parameters will be invoked to instantiate each class. 6.19. Examples The integration-tests folder of the codebase provides various examples that demonstrate how to use this extension extensively. 6.20. Common problems and troubleshooting Some issues may appear during the development, testing, and native image building process of your quarkus-cxf project; below are some common ones and how to address them. 6.20.1. REST and SOAP Endpoints Sometimes a REST endpoint may be needed in the same project where the Quarkus CXF Extension is used. The REST endpoint path must be different from the SOAP endpoint path (in order to avoid request forwarding conflicts between both protocols). For example, if a WeatherWebService interface is declared in a WSDL, you can begin by creating the org.acme.cxf.WeatherWebServiceImpl class as follows: package org.acme.cxf; import ... @Slf4j @WebService(endpointInterface = "org.acme.cxf.WeatherWebService") public class WeatherWebServiceImpl implements WeatherWebService { @Inject BackEndWeatherService backEndWeatherService; private Map<String, DailyTemperature> dailyTempByZipCode = Collections.synchronizedMap(new LinkedHashMap<>()); public WeatherWebServiceImpl() { this.dailyTempByZipCode.addAll( this.backEndWeatherService.getDailyForecast(Instant.now())); } @Override public DailyTemperature estimationTemperatures(String zipCode) { log.info("Daily estimation temperatures forecast called with '{}' zip code paramter", zipCode); return this.dailyTempByZipCode.get(zipCode); } } After that, you would need to specify the root context for your CXF web services, as indicated in the configuration documentation to split the REST (with RESTEasy for example) and SOAP routes based on their root context paths. CXF's SOAP properties: quarkus.cxf.path=/soap quarkus.cxf.endpoint."/weather".implementor=org.acme.cxf.WeatherWebServiceImpl Now, imagine the following RESTEasy endpoint: package org.acme.reasteasy; import ... @Slf4j @Path("/healthcheck") public class HealthCheckResource { @Inject BackEndWeatherService backEndWeatherService; @GET public Response doHealthCheck() { if(this.backEndWeatherService.isAvailable()) { return Response.ok().build(); } else { return Response.status(Response.Status.SERVICE_UNAVAILABLE); } } } You can separate your REST endpoint by configuring the REASTEasy path: quarkus.resteasy.path=/rest You should now be able to send requests to both your REST and SOAP endpoints deployed within a single project, at: http://localhost:8080/rest/healthcheck for REST http://localhost:8080/soap/weather for SOAP 6.20.2. Non ASCII Characters Sometimes the wsdl2java autogenerated Java classes may not be fully compatible with GraalVM due to non ASCII characters getting included in the code. Similar exceptions to the below may appear during native image builds. [quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26] compile: 161 459,15 ms, 8,54 GB [quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26] image: 158 272,73 ms, 8,43 GB [quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26] write: 205,82 ms, 8,43 GB Fatal error:com.oracle.svm.core.util.VMErrorUSDHostedError: java.lang.RuntimeException: oops : expected ASCII string! com.oracle.svm.reflect.OperationOrderStatusType_CREE_f151156b0d42ecdbdfb919501d8a86dda8733012_1456.hashCode at com.oracle.svm.core.util.VMError.shouldNotReachHere(VMError.java:72) Below is an example of auto-generated non ASCII characters in a Java class: @XmlType(name = "OperationOrderStatusType") @XmlEnum public enum OperationOrderStatusType { @XmlEnumValue("Cr\u00e9\u00e9") CREE("Cr\u00e9\u00e9"), @XmlEnumValue("A communiquer") A_COMMUNIQUER("A communiquer"), @XmlEnumValue("En attente de r\u00e9ponse") EN_ATTENTE_DE_REPONSE("En attente de r\u00e9ponse"), @XmlEnumValue("Attribu\u00e9") ATTRIBUE("Attribu\u00e9"), @XmlEnumValue("Clotur\u00e9") CLOTURE("Clotur\u00e9"), @XmlEnumValue("Annul\u00e9") ANNULE("Annul\u00e9"); private final String value; OperationOrderStatusType(String v) { value = v; } public String value() { return value; } public static OperationOrderStatusType fromValue(String v) { for (OperationOrderStatusType c: OperationOrderStatusType.values()) { if (c.value.equals(v)) { return c; } } throw new IllegalArgumentException(v); } } Anything starting with \u will be a problem. Consequently the following refactoring is needed: @XmlType(name = "OperationOrderStatusType") @XmlEnum public enum OperationOrderStatusType { @XmlEnumValue("Cree") CREE("Cree"), @XmlEnumValue("A communiquer") A_COMMUNIQUER("A communiquer"), @XmlEnumValue("En attente de reponse") EN_ATTENTE_DE_REPONSE("En attente de reponse"), @XmlEnumValue("Attribue") ATTRIBUE("Attribue"), @XmlEnumValue("Cloture") CLOTURE("Cloture"), @XmlEnumValue("Annule") ANNULE("Annule"); private final String value; OperationOrderStatusType(String v) { value = v; } public String value() { return value; } public static OperationOrderStatusType fromValue(String v) { for (OperationOrderStatusType c: OperationOrderStatusType.values()) { if (c.value.equals(v)) { return c; } } throw new IllegalArgumentException(v); } } 6.21. Camel Integration https//camel.apache.org/camel-quarkus/latest/index.html[Camel Quarkus] supports CXF since its version 2.12.0. Under the hood, the implementation is based on Quarkus CXF. Therefore, all functionality available in Quarkus CXF is also available in Camel Quarkus. Please refer to https//camel.apache.org/camel-quarkus/latest/reference/extensions/cxf-soap.html[Camel Quarkus CXF SOAP] extension documentation for further details.

Commands:
<project ...> <properties> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version><!-- Check the latest https://repo1.maven.org/maven2/io/quarkus/platform/quarkus-cxf-bom/ --></quarkus.platform.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>USD{quarkus.platform.artifact-id}</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>quarkus-cxf-bom</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
package io.quarkiverse.cxf.it.server; import jakarta.jws.WebMethod; import jakarta.jws.WebService; /** * The simplest Hello service. */ @WebService(name = "HelloService", serviceName = "HelloService") public interface HelloService { @WebMethod String hello(String text); }
package io.quarkiverse.cxf.it.server; import jakarta.jws.WebMethod; import jakarta.jws.WebService; /** * The simplest Hello service implementation. */ @WebService(serviceName = "HelloService", portName = "HelloServicePort") public class HelloServiceImpl implements HelloService { @WebMethod @Override public String hello(String text) { return "Hello " + text + "!"; } }
The context path under which all services will be available quarkus.cxf.path = /soap Publish "HelloService" under the context path /USD{quarkus.cxf.path}/hello quarkus.cxf.endpoint."/hello".implementor = io.quarkiverse.cxf.it.server.HelloServiceImpl quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature
mvn quarkus:dev
curl http://localhost:8080/soap/hello?wsdl <?xml version='1.0' encoding='UTF-8'?> <wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://server.it.cxf.quarkiverse.io/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="HelloService" targetNamespace="http://server.it.cxf.quarkiverse.io/"> <wsdl:types> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://server.it.cxf.quarkiverse.io/" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://server.it.cxf.quarkiverse.io/"> <xsd:element name="hello" type="tns:hello"/> <xsd:complexType name="hello"> <xsd:sequence> <xsd:element minOccurs="0" name="arg0" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:element name="helloResponse" type="tns:helloResponse"/> <xsd:complexType name="helloResponse"> <xsd:sequence> <xsd:element minOccurs="0" name="return" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema> </wsdl:types> <wsdl:message name="helloResponse"> <wsdl:part element="tns:helloResponse" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:message name="hello"> <wsdl:part element="tns:hello" name="parameters"> </wsdl:part> </wsdl:message> <wsdl:portType name="HelloService"> <wsdl:operation name="hello"> <wsdl:input message="tns:hello" name="hello"> </wsdl:input> <wsdl:output message="tns:helloResponse" name="helloResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="HelloServiceSoapBinding" type="tns:HelloService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="hello"> <soap:operation soapAction="" style="document"/> <wsdl:input name="hello"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="helloResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="HelloService"> <wsdl:port binding="tns:HelloServiceSoapBinding" name="HelloServicePort"> <soap:address location="http://localhost:8080/soap/hello"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" -d '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body><ns2:hello xmlns:ns2="http://server.it.cxf.quarkiverse.io/"><arg0>World</arg0></ns2:hello></soap:Body> </soap:Envelope>' http://localhost:8080/soap/hello <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns1:helloResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <return>Hello World!</return> </ns1:helloResponse> </soap:Body> </soap:Envelope>
<dependency> <groupId>io.quarkiverse.cxf</groupId> <artifactId>quarkus-cxf-rt-features-logging</artifactId> </dependency>
quarkus.cxf.endpoint."/hello".features=org.apache.cxf.ext.logging.LoggingFeature
2023-01-11 22:12:21,315 INFO [org.apa.cxf.ser.Hel.REQ_IN] (vert.x-worker-thread-0) REQ_IN Address: http://localhost:8080/soap/hello HttpMethod: POST Content-Type: text/xml;charset=UTF-8 ExchangeId: af10747a-8477-4c17-bf5f-2a4a3a95d61c ServiceName: HelloService PortName: HelloServicePort PortTypeName: HelloService Headers: {Accept=*/*, User-Agent=curl/7.79.1, content-type=text/xml;charset=UTF-8, Host=localhost:8080, Content-Length=203, x-quarkus-hot-deployment-done=true} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body><ns2:hello xmlns:ns2="http://server.it.cxf.quarkiverse.io/"><arg0>World</arg0></ns2:hello></soap:Body> </soap:Envelope> 2023-01-11 22:12:21,327 INFO [org.apa.cxf.ser.Hel.RESP_OUT] (vert.x-worker-thread-0) RESP_OUT Address: http://localhost:8080/soap/hello Content-Type: text/xml ResponseCode: 200 ExchangeId: af10747a-8477-4c17-bf5f-2a4a3a95d61c ServiceName: HelloService PortName: HelloServicePort PortTypeName: HelloService Headers: {} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns1:helloResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"><return>Hello World!</return></ns1:helloResponse></soap:Body></soap:Envelope>
docker run -p 8082:8080 quay.io/l2x6/calculator-ws:1.0
curl -s http://localhost:8082/calculator-ws/CalculatorService?wsdl <?xml version="1.0" ?> <wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="CalculatorService" targetNamespace="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator"> <wsdl:binding name="CalculatorServiceSoapBinding" type="tns:CalculatorService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding> <wsdl:operation name="add"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="add"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="addResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> <wsdl:operation name="subtract"> <soap:operation soapAction="" style="document"></soap:operation> <wsdl:input name="subtract"> <soap:body use="literal"></soap:body> </wsdl:input> <wsdl:output name="subtractResponse"> <soap:body use="literal"></soap:body> </wsdl:output> </wsdl:operation> </wsdl:binding> </wsdl:definitions>
curl -s -X POST -H "Content-Type: text/xml;charset=UTF-8" -d '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <add xmlns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator"> <arg0 xmlns="">7</arg0> 1 <arg1 xmlns="">4</arg1> </add> </Body> </Envelope>' http://localhost:8082/calculator-ws/CalculatorService <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:addResponse xmlns:ns2="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator"> <return>11</return> 2 </ns2:addResponse> </soap:Body> </soap:Envelope>
package io.quarkiverse.cxf.client.it; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService; import io.quarkiverse.cxf.annotation.CXFClient; @Path("/cxf/calculator-client") public class CxfClientRestResource { @CXFClient("myCalculator") 1 CalculatorService myCalculator; @GET @Path("/add") @Produces(MediaType.TEXT_PLAIN) public int add(@QueryParam("a") int a, @QueryParam("b") int b) { return myCalculator.add(a, b); 2 } }
cxf.it.calculator.baseUri=http://localhost:8082 quarkus.cxf.client.myCalculator.wsdl = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.myCalculator.client-endpoint-url = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService quarkus.cxf.client.myCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService
mvn quarkus:dev INFO [io.quarkus] (Quarkus Main Thread) ... Listening on: http://localhost:8080
curl -s 'http://localhost:8080/cxf/calculator-client/add?a=5&b=6' 11
bean reference by type quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature
bean reference by bean name quarkus.cxf.endpoint."/fruit".features = #myCustomLoggingFeature
import org.apache.cxf.ext.logging.LoggingFeature; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; class Producers { @Produces @ApplicationScoped @Named("myCustomLoggingFeature") LoggingFeature myCustomLoggingFeature() { LoggingFeature loggingFeature = new LoggingFeature(); loggingFeature.setPrettyLogging(true); return loggingFeature; } }
mvn package
ls -lh target/quarkus-app drwxr-xr-x. 2 ppalaga ppalaga 4.0K Jan 12 22:29 app drwxr-xr-x. 4 ppalaga ppalaga 4.0K Jan 12 22:29 lib drwxr-xr-x. 2 ppalaga ppalaga 4.0K Jan 12 22:29 quarkus -rw-r-r--. 1 ppalaga ppalaga 6.1K Jan 12 22:29 quarkus-app-dependencies.txt -rw-r-r--. 1 ppalaga ppalaga 678 Jan 12 22:29 quarkus-run.jar
java -jar target/quarkus-app/quarkus-run.jar
<profile> <id>native</id> <activation> <property> <name>native</name> </property> </activation> <properties> <skipITs>false</skipITs> <quarkus.package.type>native</quarkus.package.type> </properties> </profile>
Make sure USDGRAALVM_HOME is set properly echo USDGRAALVM_HOME /home/{user}/.sdkman/candidates/java/{major}.{minor}.r{java-version}-grl Produce the native executable mvn package -Pnative
Produce the native executable mvn package -Pnative -Dquarkus.native.container-build=true
ls -l target -rwxr-xr-x. 1 ppalaga ppalaga 71M Jan 11 22:42 quarkus-cxf-integration-test-server-1.8.0-SNAPSHOT-runner
target/*-runner INFO [io.quarkus] (main) quarkus-cxf-integration-test-server 1.8.0-SNAPSHOT native (powered by Quarkus 2.15.2.Final) started in 0.042s. Listening on: http://0.0.0.0:8080
Global settings quarkus.cxf.logging.enabled-for = both quarkus.cxf.logging.pretty = true
For a service: quarkus.cxf.endpoint."/hello".logging.enabled = true quarkus.cxf.endpoint."/hello".logging.pretty = true For a client: quarkus.cxf.client.hello.logging.enabled = true quarkus.cxf.client.hello.logging.pretty = true
For a service: quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature For a client: quarkus.cxf.client."myClient".features = org.apache.cxf.ext.logging.LoggingFeature
@org.apache.cxf.feature.Features (features = {"org.apache.cxf.ext.logging.LoggingFeature"}) @WebService(endpointInterface = "org.acme.SayHi", targetNamespace = "uri:org.acme") public class SayHiImplementation implements SayHi { public long sayHi(long arg) { return arg; } // }
import org.apache.cxf.ext.logging.LoggingFeature; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; class Producers { @Produces @ApplicationScoped @Named("limitedLoggingFeature") // "limitedLoggingFeature" is redundant if the name of the method is the same LoggingFeature limitedLoggingFeature() { LoggingFeature loggingFeature = new LoggingFeature(); loggingFeature.setPrettyLogging(true); loggingFeature.setLimit(1024); return loggingFeature; } }
For a service: quarkus.cxf.endpoint."/hello".features = #limitedLoggingFeature For a client: quarkus.cxf.client.hello.features = #limitedLoggingFeature
package io.quarkiverse.cxf.it.server; import java.util.Objects; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @XmlType(name = "Fruit") @XmlRootElement public class Fruit { private String name; private String description; public Fruit() { } public Fruit(String name, String description) { this.name = name; this.description = description; } public String getName() { return name; } @XmlElement public void setName(String name) { this.name = name; } public String getDescription() { return description; } @XmlElement public void setDescription(String description) { this.description = description; } @Override public boolean equals(Object obj) { if (!(obj instanceof Fruit)) { return false; } Fruit other = (Fruit) obj; return Objects.equals(other.getName(), this.getName()); } @Override public int hashCode() { return Objects.hash(this.getName()); } }
package io.quarkiverse.cxf.it.server; import java.util.Set; import jakarta.jws.WebMethod; import jakarta.jws.WebService; @WebService public interface FruitService { @WebMethod Set<Fruit> list(); @WebMethod Set<Fruit> add(Fruit fruit); @WebMethod Set<Fruit> delete(Fruit fruit); }
package io.quarkiverse.cxf.it.server; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import jakarta.jws.WebService; @WebService(serviceName = "FruitService") public class FruitServiceImpl implements FruitService { private Set<Fruit> fruits = Collections.synchronizedSet(new LinkedHashSet<>()); public FruitServiceImpl() { fruits.add(new Fruit("Apple", "Winter fruit")); fruits.add(new Fruit("Pineapple", "Tropical fruit")); } @Override public Set<Fruit> list() { return fruits; } @Override public Set<Fruit> add(Fruit fruit) { fruits.add(fruit); return fruits; } @Override public Set<Fruit> delete(Fruit fruit) { fruits.remove(fruit); return fruits; } }
quarkus.cxf.endpoint."/fruits".implementor = io.quarkiverse.cxf.it.server.FruitServiceImpl quarkus.cxf.endpoint."/fruits".features = org.apache.cxf.ext.logging.LoggingFeature
mvn quarkus:dev INFO [io.quarkus] (Quarkus Main Thread) ... Listening on: http://localhost:8080
curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" -d '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <soapenv:Body> <ns1:list/> </soapenv:Body> </soapenv:Envelope>' http://localhost:8080/soap/fruits <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns1:listResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Winter fruit</description> <name>Apple</name> </return> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Tropical fruit</description> <name>Pineapple</name> </return> </ns1:listResponse> </soap:Body> </soap:Envelope>
curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" -d '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:add xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <arg0> <description>Mediterranean fruit</description> <name>Orange</name> </arg0> </ns2:add> </soap:Body></soap:Envelope>' http://localhost:8080/soap/fruits <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns1:addResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Winter fruit</description> <name>Apple</name> </return> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Tropical fruit</description> <name>Pineapple</name> </return> <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/"> <description>Mediterranean fruit</description> <name>Orange</name> </return> </ns1:addResponse> </soap:Body> </soap:Envelope>
keystore.type = jks 1 quarkus.cxf.client.hello.client-endpoint-url = https://localhost:USD{quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.hello.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.hello.trust-store-type = USD{keystore.type} 2 quarkus.cxf.client.hello.trust-store = client-truststore.USD{keystore.type} quarkus.cxf.client.hello.trust-store-password = client-truststore-password
1 quarkus.http.ssl.certificate.key-store-file = localhost-keystore.USD{keystore.type} quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password quarkus.http.ssl.certificate.key-store-key-alias = localhost quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password
The store type could also be jks keystore.type = pkcs12 Server keystore for Simple TLS quarkus.http.ssl.certificate.key-store-file = localhost-keystore.USD{keystore.type} quarkus.http.ssl.certificate.key-store-password = localhost-keystore-password quarkus.http.ssl.certificate.key-store-key-alias = localhost quarkus.http.ssl.certificate.key-store-key-password = localhost-keystore-password Server truststore for Mutual TLS quarkus.http.ssl.certificate.trust-store-file = localhost-truststore.USD{keystore.type} quarkus.http.ssl.certificate.trust-store-password = localhost-truststore-password Do not allow any clients which do not prove their indentity through an SSL certificate quarkus.http.ssl.client-auth = required CXF service quarkus.cxf.endpoint."/mTls".implementor = io.quarkiverse.cxf.it.auth.mtls.MTlsHelloServiceImpl CXF client with a properly set certificate for mTLS quarkus.cxf.client.mTls.client-endpoint-url = https://localhost:USD{quarkus.http.test-ssl-port}/services/mTls quarkus.cxf.client.mTls.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.mTls.key-store = target/classes/client-keystore.USD{keystore.type} quarkus.cxf.client.mTls.key-store-type = USD{keystore.type} quarkus.cxf.client.mTls.key-store-password = client-keystore-password quarkus.cxf.client.mTls.key-password = client-keystore-password quarkus.cxf.client.mTls.trust-store = target/classes/client-truststore.USD{keystore.type} quarkus.cxf.client.mTls.trust-store-type = USD{keystore.type} quarkus.cxf.client.mTls.trust-store-password = client-truststore-password Include the keystores in the native executable quarkus.native.resources.includes = *.pkcs12,*.jks
<?xml version="1.0" encoding="UTF-8"?> <wsp:Policy wsp:Id="HttpsSecurityServicePolicy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding> <wsp:Policy> <sp:TransportToken> <wsp:Policy> <sp:HttpsToken RequireClientCertificate="false" /> </wsp:Policy> </sp:TransportToken> <sp:IncludeTimestamp /> <sp:AlgorithmSuite> <wsp:Policy> <sp:Basic128 /> </wsp:Policy> </sp:AlgorithmSuite> </wsp:Policy> </sp:TransportBinding> </wsp:All> </wsp:ExactlyOne> </wsp:Policy>
package io.quarkiverse.cxf.it.security.policy; import jakarta.jws.WebMethod; import jakarta.jws.WebService; import org.apache.cxf.annotations.Policy; /** * A service implementation with a transport policy set */ @WebService(serviceName = "HttpsPolicyHelloService") @Policy(placement = Policy.Placement.BINDING, uri = "https-policy.xml") public interface HttpsPolicyHelloService extends AbstractHelloService { @WebMethod @Override public String hello(String text); }
ERROR [org.apa.cxf.ws.pol.PolicyVerificationInInterceptor] Inbound policy verification failed: These policy alternatives can not be satisfied: {http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}TransportBinding: TLS is not enabled
quarkus.cxf.client.basicAuth.wsdl = http://localhost:USD{quarkus.http.test-port}/soap/basicAuth?wsdl quarkus.cxf.client.basicAuth.client-endpoint-url = http://localhost:USD{quarkus.http.test-port}/soap/basicAuth quarkus.cxf.client.basicAuth.username = bob quarkus.cxf.client.basicAuth.password = bob234
quarkus.cxf.client.basicAuthSecureWsdl.wsdl = http://localhost:USD{quarkus.http.test-port}/soap/basicAuth?wsdl quarkus.cxf.client.basicAuthSecureWsdl.client-endpoint-url = http://localhost:USD{quarkus.http.test-port}/soap/basicAuthSecureWsdl quarkus.cxf.client.basicAuthSecureWsdl.username = bob quarkus.cxf.client.basicAuthSecureWsdl.password = USD{client-server.bob.password} quarkus.cxf.client.basicAuthSecureWsdl.secure-wsdl-access = true
quarkus.http.auth.basic = true quarkus.security.users.embedded.enabled = true quarkus.security.users.embedded.plain-text = true quarkus.security.users.embedded.users.alice = alice123 quarkus.security.users.embedded.roles.alice = admin quarkus.security.users.embedded.users.bob = bob234 quarkus.security.users.embedded.roles.bob = app-user
package io.quarkiverse.cxf.it.auth.basic; import jakarta.annotation.security.RolesAllowed; import jakarta.jws.WebService; import io.quarkiverse.cxf.it.HelloService; @WebService(serviceName = "HelloService", targetNamespace = HelloService.NS) @RolesAllowed("app-user") public class BasicAuthHelloServiceImpl implements HelloService { @Override public String hello(String person) { return "Hello " + person + "!"; } }
cxf.it.calculator.baseUri = http://localhost:8082 quarkus.cxf.client.myCalculator.wsdl = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl quarkus.cxf.client.myCalculator.client-endpoint-url = USD{cxf.it.calculator.baseUri}/calculator-ws/CalculatorService quarkus.cxf.client.myCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService another client quarkus.cxf.client.anotherCalculator.wsdl = https://acme.com/ws/WeatherService?wsdl quarkus.cxf.client.anotherCalculator.client-endpoint-url = https://acme.com/ws/WeatherService quarkus.cxf.client.anotherCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService
void onStart(@Observes StartupEvent ev) { HTTPConduitConfigurer httpConduitConfigurer = new HTTPConduitConfigurer() { public void configure(String name, String address, HTTPConduit c) { AsyncHTTPConduit conduit = (AsyncHTTPConduit)c; // use setter to configure client conduit.getHttpAsyncClient().getCredentialsProvider().setCredentials( AuthScope.ANY, new NTCredentials( USER,PWD, "", DOM ) ); conduit.getClient().setAllowChunking( false ); conduit.getClient().setAutoRedirect( true ); } }; final Bus bus = BusFactory.getDefaultBus(); bus.setExtension(httpConduitConfigurer, HTTPConduitConfigurer.class); }
quarkus.http.host-enabled = false
import java.net.URL; import javax.xml.namespace.QName; import jakarta.xml.ws.Service; import java.io.Closeable; final URL serviceUrl = new URL("http://localhost/myService?wsdl"); final QName qName = new QName("http://acme.org/myNamespace", "MyService"); final Service service = jakarta.xml.ws.Service.create(serviceUrl, qName); final MyService proxy = service.getPort(MyService.class); try { proxy.doSomething(); } finally { ((Closeable) proxy).close(); }
quarkus.http.proxy.proxy-address-forwarding = true quarkus.http.proxy.enable-forwarded-host = true quarkus.http.proxy.enable-forwarded-prefix = true
X-Forwarded-Proto: https X-Forwarded-Host: api.example.com X-Forwarded-Port: 443 X-Forwarded-Prefix: /my-prefix
<soap:address location="https://api.example.com:443/my-prefix/services/my-service"/>
<plugin> <groupId>io.quarkus</groupId> <artifactId>quarkus-maven-plugin</artifactId> <executions> <execution> <goals> <goal>build</goal> <goal>generate-code</goal> </goals> </execution> </executions> </plugin>
quarkus.cxf.codegen.wsdl2java.includes = wsdl/*.wsdl
Parameters for foo.wsdl quarkus.cxf.codegen.wsdl2java.foo-params.includes = wsdl/foo.wsdl quarkus.cxf.codegen.wsdl2java.foo-params.wsdl-location = wsdl/foo.wsdl Parameters for bar.wsdl quarkus.cxf.codegen.wsdl2java.bar-params.includes = wsdl/bar.wsdl quarkus.cxf.codegen.wsdl2java.bar-params.wsdl-location = wsdl/bar.wsdl quarkus.cxf.codegen.wsdl2java.bar-params.xjc = ts
quarkus.cxf.java2ws.includes = io.quarkiverse.cxf.it.server.HelloService,io.quarkiverse.cxf.it.server.FaultyHelloService quarkus.cxf.java2ws.wsdl-name-template = %TARGET_DIR%/Java2wsTest/%SIMPLE_CLASS_NAME%-from-java2ws.wsdl
@org.apache.cxf.feature.Features (features = {"org.apache.cxf.ext.logging.LoggingFeature"}) @org.apache.cxf.interceptor.InInterceptors (interceptors = {"org.acme.Test1Interceptor" }) @org.apache.cxf.interceptor.InFaultInterceptors (interceptors = {"org.acme.Test2Interceptor" }) @org.apache.cxf.interceptor.OutInterceptors (interceptors = {"org.acme.Test1Interceptor" }) @org.apache.cxf.interceptor.InFaultInterceptors (interceptors = {"org.acme.Test2Interceptor","org.acme.Test3Intercetpor" }) @WebService(endpointInterface = "org.acme.SayHi", targetNamespace = "uri:org.acme") public class SayHiImplementation implements SayHi { public long sayHi(long arg) { return arg; } // }
quarkus.cxf.endpoint."/greeting-service".features=org.apache.cxf.ext.logging.LoggingFeature quarkus.cxf.endpoint."/greeting-service".in-interceptors=org.acme.Test1Interceptor quarkus.cxf.endpoint."/greeting-service".out-interceptors=org.acme.Test1Interceptor quarkus.cxf.endpoint."/greeting-service".in-fault-interceptors=org.acme.Test2Interceptor,org.acme.Test3Intercetpor quarkus.cxf.endpoint."/greeting-service".out-fault-interceptors=org.acme.Test1Intercetpor
A web service endpoint with multiple Handler classes quarkus.cxf.endpoint."/greeting-service".handlers=org.acme.MySOAPHandler,org.acme.AnotherSOAPHandler A web service client with a single Handler quarkus.cxf.client."greeting-client".handlers=org.acme.MySOAPHandler
import jakarta.xml.ws.handler.soap.SOAPHandler; import jakarta.xml.ws.handler.soap.SOAPMessageContext; public class MySOAPHandler implements SOAPHandler<SOAPMessageContext> { public boolean handleMessage(SOAPMessageContext messageContext) { SOAPMessage msg = messageContext.getMessage(); return true; } // other methods }
import jakarta.xml.transform.stream.StreamSource; import jakarta.xml.ws.BindingType; import jakarta.xml.ws.Provider; import jakarta.xml.ws.Service; import jakarta.xml.ws.ServiceMode; import jakarta.xml.ws.WebServiceProvider; import java.io.StringReader; @WebServiceProvider @ServiceMode(value = Service.Mode.PAYLOAD) public class StreamSourcePayloadProvider implements Provider<StreamSource> { public StreamSourcePayloadProvider() { } public StreamSource invoke(StreamSource request) { String payload = StaxUtils.toString(request); // Do some interesting things StreamSource response = new StreamSource(new StringReader(payload)); return response; } }
A web service endpoint with the Provider implementation class quarkus.cxf.endpoint."/stream-source".implementor=org.acme.StreamSourcePayloadProvider
package org.acme.cxf; import @Slf4j @WebService(endpointInterface = "org.acme.cxf.WeatherWebService") public class WeatherWebServiceImpl implements WeatherWebService { @Inject BackEndWeatherService backEndWeatherService; private Map<String, DailyTemperature> dailyTempByZipCode = Collections.synchronizedMap(new LinkedHashMap<>()); public WeatherWebServiceImpl() { this.dailyTempByZipCode.addAll( this.backEndWeatherService.getDailyForecast(Instant.now())); } @Override public DailyTemperature estimationTemperatures(String zipCode) { log.info("Daily estimation temperatures forecast called with '{}' zip code paramter", zipCode); return this.dailyTempByZipCode.get(zipCode); } }
quarkus.cxf.path=/soap quarkus.cxf.endpoint."/weather".implementor=org.acme.cxf.WeatherWebServiceImpl
package org.acme.reasteasy; import @Slf4j @Path("/healthcheck") public class HealthCheckResource { @Inject BackEndWeatherService backEndWeatherService; @GET public Response doHealthCheck() { if(this.backEndWeatherService.isAvailable()) { return Response.ok().build(); } else { return Response.status(Response.Status.SERVICE_UNAVAILABLE); } } }
quarkus.resteasy.path=/rest
[quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26] compile: 161 459,15 ms, 8,54 GB [quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26] image: 158 272,73 ms, 8,43 GB [quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26] write: 205,82 ms, 8,43 GB Fatal error:com.oracle.svm.core.util.VMErrorUSDHostedError: java.lang.RuntimeException: oops : expected ASCII string! com.oracle.svm.reflect.OperationOrderStatusType_CREE_f151156b0d42ecdbdfb919501d8a86dda8733012_1456.hashCode at com.oracle.svm.core.util.VMError.shouldNotReachHere(VMError.java:72)
@XmlType(name = "OperationOrderStatusType") @XmlEnum public enum OperationOrderStatusType { @XmlEnumValue("Cr\u00e9\u00e9") CREE("Cr\u00e9\u00e9"), @XmlEnumValue("A communiquer") A_COMMUNIQUER("A communiquer"), @XmlEnumValue("En attente de r\u00e9ponse") EN_ATTENTE_DE_REPONSE("En attente de r\u00e9ponse"), @XmlEnumValue("Attribu\u00e9") ATTRIBUE("Attribu\u00e9"), @XmlEnumValue("Clotur\u00e9") CLOTURE("Clotur\u00e9"), @XmlEnumValue("Annul\u00e9") ANNULE("Annul\u00e9"); private final String value; OperationOrderStatusType(String v) { value = v; } public String value() { return value; } public static OperationOrderStatusType fromValue(String v) { for (OperationOrderStatusType c: OperationOrderStatusType.values()) { if (c.value.equals(v)) { return c; } } throw new IllegalArgumentException(v); } }
@XmlType(name = "OperationOrderStatusType") @XmlEnum public enum OperationOrderStatusType { @XmlEnumValue("Cree") CREE("Cree"), @XmlEnumValue("A communiquer") A_COMMUNIQUER("A communiquer"), @XmlEnumValue("En attente de reponse") EN_ATTENTE_DE_REPONSE("En attente de reponse"), @XmlEnumValue("Attribue") ATTRIBUE("Attribue"), @XmlEnumValue("Cloture") CLOTURE("Cloture"), @XmlEnumValue("Annule") ANNULE("Annule"); private final String value; OperationOrderStatusType(String v) { value = v; } public String value() { return value; } public static OperationOrderStatusType fromValue(String v) { for (OperationOrderStatusType c: OperationOrderStatusType.values()) { if (c.value.equals(v)) { return c; } } throw new IllegalArgumentException(v); } }