This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus-examples.git
commit 18b935d4142dcbb85a6e177ea9fe5b7dd9b7f88e Author: Lukas Lowinger <[email protected]> AuthorDate: Tue Dec 2 13:03:25 2025 +0100 [resolves #7918] Demonstrate using CXF client producer --- cxf-soap/README.adoc | 37 +++++++++++ cxf-soap/pom.xml | 8 +++ ...ilder.java => PojoCxfConsumerRouteBuilder.java} | 2 +- .../cxf/soap/pojo/PojoCxfProducerRouteBuilder.java | 70 ++++++++++++++++++++ .../org/acme/cxf/soap/pojo/service/Contact.java | 3 + .../org/acme/cxf/soap/pojo/service/Contacts.java | 7 +- .../org/acme/cxf/soap/utils/CxfServerUtils.java} | 11 ++-- .../soap/{PojoClientTest.java => PojoTest.java} | 74 +++++++++++++++++----- .../{PojoClientTestIT.java => PojoTestIT.java} | 2 +- .../java/org/acme/cxf/soap/WsdlClientTest.java | 5 +- 10 files changed, 192 insertions(+), 27 deletions(-) diff --git a/cxf-soap/README.adoc b/cxf-soap/README.adoc index 981ec6ca..72b4b552 100644 --- a/cxf-soap/README.adoc +++ b/cxf-soap/README.adoc @@ -4,6 +4,8 @@ {cq-description} In this example we will create two SOAP webservices with two different approaches. Both services will use Camel routes as service implementation exposed via CXF component. +We will then communicate with the SOAP webservices (Camel consumer) directly via SOAP messages (ie. with `curl`). +Later we will also leverage Camel producer to send SOAP messages to our exposed SOAP webservices. == WSDL first @@ -36,6 +38,20 @@ The exposed contact web service will enable five operations - `addContact`, `get TIP: If you would like to only generate WSDL from Java, you can follow the https://quarkiverse.github.io/quarkiverse-docs/quarkus-cxf/dev/user-guide/generate-wsdl-from-java.html[WSDL from Java] chapter of Quarkus CXF documentation. +== Camel producer +The approach mentioned above concerned the creation of SOAP webservices. +We will also describe how to create a client using the Camel Quarkus CXF SOAP producer. +You can observe `org.acme.cxf.soap.pojo.PojoCxfProducerRouteBuilder` where we are creating the `CxfEndpoint` which is used in the route in form of Camel producer. + +It can typically looks like: +[source,java] +---- +from("direct:contact") + .to("cxf:bean:soapClientEndpointPojo"); +---- + +For purpose of easier testing this approach we also added Camel Quarkus REST endpoint which will be used as external entry point for invoking the producer. + == Start in the Development mode [source,shell] @@ -52,6 +68,7 @@ https://camel.apache.org/camel-quarkus/latest/first-steps.html#_development_mode [[playground]] == Playground +=== Interacting directly via SOAP messages We can first try to add a contact with: @@ -153,6 +170,26 @@ $ curl "http://localhost:8080/cxf/services/contact?wsdl" $ curl "http://localhost:8080/cxf/services/customer?wsdl" ---- +=== Interacting via Camel Quarkus CXF producer +We can also use Camel producer to interact with our exposed SOAP webservice. +For testing purpose, we've also exposed REST endpoints to easily approach it externally. + +Firstly we will add new contact with: +[source, shell] +---- +$ curl -X POST -H "Content-Type: application/json" --data '{"name":"Lukas","address":{"city":"Czech Republic","street":"Random Street"},"type":"OTHER"}' "http://localhost:8080/producer/contact?operationName=addContact" +---- +Then we will request all contacts with: +[source, shell] +---- +$ curl -X GET "http://localhost:8080/producer/contacts?operationName=getContacts" +---- + +Which should return something like: +``` +{"contacts":[{"name":"Lukas","address":{"city":"Czech Republic","street":"Random Street"},"type":"OTHER"}]} +``` + == Package and run the application Once you are done with playing/developing you may want to package and run the application for production usage. diff --git a/cxf-soap/pom.xml b/cxf-soap/pom.xml index 97d82aed..625448b5 100644 --- a/cxf-soap/pom.xml +++ b/cxf-soap/pom.xml @@ -80,6 +80,14 @@ <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-direct</artifactId> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-rest</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jackson</artifactId> + </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-bean</artifactId> diff --git a/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/MyPojoRouteBuilder.java b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/PojoCxfConsumerRouteBuilder.java similarity index 96% rename from cxf-soap/src/main/java/org/acme/cxf/soap/pojo/MyPojoRouteBuilder.java rename to cxf-soap/src/main/java/org/acme/cxf/soap/pojo/PojoCxfConsumerRouteBuilder.java index 37b26acf..dcec4cfa 100644 --- a/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/MyPojoRouteBuilder.java +++ b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/PojoCxfConsumerRouteBuilder.java @@ -27,7 +27,7 @@ import org.apache.camel.component.cxf.jaxws.CxfEndpoint; * This class demonstrate how to expose a SOAP endpoint starting from java classes */ @ApplicationScoped -public class MyPojoRouteBuilder extends RouteBuilder { +public class PojoCxfConsumerRouteBuilder extends RouteBuilder { @Produces @ApplicationScoped diff --git a/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/PojoCxfProducerRouteBuilder.java b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/PojoCxfProducerRouteBuilder.java new file mode 100644 index 00000000..8db54754 --- /dev/null +++ b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/PojoCxfProducerRouteBuilder.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.acme.cxf.soap.pojo; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Named; +import org.acme.cxf.soap.pojo.service.Contact; +import org.acme.cxf.soap.pojo.service.ContactService; +import org.acme.cxf.soap.pojo.service.Contacts; +import org.acme.cxf.soap.utils.CxfServerUtils; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.cxf.common.DataFormat; +import org.apache.camel.component.cxf.jaxws.CxfEndpoint; +import org.apache.camel.model.dataformat.JsonLibrary; +import org.apache.camel.model.rest.RestBindingMode; + +/** + * This class demonstrate how to use camel-quarkus-cxf-soap client + */ +@ApplicationScoped +public class PojoCxfProducerRouteBuilder extends RouteBuilder { + + @Produces + @ApplicationScoped + @Named + CxfEndpoint soapClientEndpointPojo() { + final CxfEndpoint result = new CxfEndpoint(); + result.setDataFormat(DataFormat.POJO); + result.setServiceClass(ContactService.class); + result.setAddress("%s/contact".formatted(CxfServerUtils.getServerUrl())); + return result; + } + + @Override + public void configure() throws Exception { + restConfiguration() + .bindingMode(RestBindingMode.json); + + rest("/producer/contact").post() + .type(Contact.class) + .to("direct:contact"); + rest("/producer/contacts").get() + .bindingMode(RestBindingMode.off) + .produces("application/json") + .to("direct:contacts"); + + from("direct:contact") + .to("cxf:bean:soapClientEndpointPojo"); + + from("direct:contacts") + .to("cxf:bean:soapClientEndpointPojo") + .convertBodyTo(Contacts.class) + .marshal().json(JsonLibrary.Jackson); + } +} diff --git a/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contact.java b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contact.java index 6c02dbf1..9f2035d0 100644 --- a/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contact.java +++ b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contact.java @@ -38,6 +38,9 @@ public class Contact { private Address address; private ContactType type; + public Contact() { + } + public String getName() { return name; } diff --git a/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contacts.java b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contacts.java index b633f779..df4612de 100644 --- a/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contacts.java +++ b/cxf-soap/src/main/java/org/acme/cxf/soap/pojo/service/Contacts.java @@ -16,6 +16,7 @@ */ package org.acme.cxf.soap.pojo.service; +import java.util.ArrayList; import java.util.Collection; import jakarta.xml.bind.annotation.XmlAccessType; @@ -28,14 +29,14 @@ import jakarta.xml.bind.annotation.XmlType; }) public class Contacts { - private Collection<Contact> contacts; + private Collection<Contact> contacts = new ArrayList<>(); public Contacts() { } public Contacts(Collection<Contact> contacts) { super(); - this.contacts = contacts; + this.contacts = (contacts != null) ? contacts : new ArrayList<>(); } public Collection<Contact> getContacts() { @@ -43,6 +44,6 @@ public class Contacts { } public void setContacts(Collection<Contact> contacts) { - this.contacts = contacts; + this.contacts = (contacts != null) ? contacts : new ArrayList<>(); } } diff --git a/cxf-soap/src/test/java/org/acme/cxf/soap/BaseTest.java b/cxf-soap/src/main/java/org/acme/cxf/soap/utils/CxfServerUtils.java similarity index 82% rename from cxf-soap/src/test/java/org/acme/cxf/soap/BaseTest.java rename to cxf-soap/src/main/java/org/acme/cxf/soap/utils/CxfServerUtils.java index 445afe6e..dec296ad 100644 --- a/cxf-soap/src/test/java/org/acme/cxf/soap/BaseTest.java +++ b/cxf-soap/src/main/java/org/acme/cxf/soap/utils/CxfServerUtils.java @@ -14,17 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.acme.cxf.soap; +package org.acme.cxf.soap.utils; import io.quarkus.runtime.LaunchMode; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; -public class BaseTest { - protected String getServerUrl() { +public final class CxfServerUtils { + private CxfServerUtils() { + } + + public static String getServerUrl() { Config config = ConfigProvider.getConfig(); final int port = LaunchMode.current().equals(LaunchMode.TEST) ? config.getValue("quarkus.http.test-port", Integer.class) : config.getValue("quarkus.http.port", Integer.class); - return String.format("http://localhost:%d", port); + return "http://localhost:%d%s".formatted(port, config.getValue("quarkus.cxf.path", String.class)); } } diff --git a/cxf-soap/src/test/java/org/acme/cxf/soap/PojoClientTest.java b/cxf-soap/src/test/java/org/acme/cxf/soap/PojoTest.java similarity index 53% rename from cxf-soap/src/test/java/org/acme/cxf/soap/PojoClientTest.java rename to cxf-soap/src/test/java/org/acme/cxf/soap/PojoTest.java index 6d846ea5..c8cf04df 100644 --- a/cxf-soap/src/test/java/org/acme/cxf/soap/PojoClientTest.java +++ b/cxf-soap/src/test/java/org/acme/cxf/soap/PojoTest.java @@ -22,21 +22,49 @@ import java.net.URL; import javax.xml.namespace.QName; import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; import jakarta.xml.ws.Service; import org.acme.cxf.soap.pojo.service.Address; import org.acme.cxf.soap.pojo.service.Contact; import org.acme.cxf.soap.pojo.service.ContactService; import org.acme.cxf.soap.pojo.service.ContactType; import org.acme.cxf.soap.pojo.service.NoSuchContactException; +import org.acme.cxf.soap.utils.CxfServerUtils; +import org.apache.camel.component.cxf.common.message.CxfConstants; +import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @QuarkusTest -public class PojoClientTest extends BaseTest { +public class PojoTest { + + protected static Contact createConsumerContact() { + Contact contact = new Contact(); + contact.setName("Croway"); + contact.setType(ContactType.OTHER); + Address address = new Address(); + address.setCity("Rome"); + address.setStreet("Test Street"); + contact.setAddress(address); + + return contact; + } + + protected static Contact createProducerContact() { + Contact contact = new Contact(); + contact.setName("Lukas"); + contact.setType(ContactType.OTHER); + Address address = new Address(); + address.setCity("Czech Republic"); + address.setStreet("Random Street"); + contact.setAddress(address); + + return contact; + } protected ContactService createCXFClient() { try { - final URL serviceUrl = new URL(getServerUrl() + "/cxf/services/contact?wsdl"); + final URL serviceUrl = new URL(CxfServerUtils.getServerUrl() + "/contact?wsdl"); final QName qName = new QName(ContactService.TARGET_NS, ContactService.class.getSimpleName()); final Service service = Service.create(serviceUrl, qName); return service.getPort(ContactService.class); @@ -45,27 +73,41 @@ public class PojoClientTest extends BaseTest { } } - protected static Contact createContact() { - Contact contact = new Contact(); - contact.setName("Croway"); - contact.setType(ContactType.OTHER); - Address address = new Address(); - address.setCity("Rome"); - address.setStreet("Test Street"); - contact.setAddress(address); + @Test + public void testPojoCamelConsumer() throws NoSuchContactException { + ContactService cxfClient = createCXFClient(); + int countContactsStart = cxfClient.getContacts().getContacts().size(); + cxfClient.addContact(createConsumerContact()); + Assertions.assertSame(countContactsStart + 1, cxfClient.getContacts().getContacts().size(), + "We should have one contact added."); - return contact; + Assertions.assertNotNull(cxfClient.getContact("Croway"), "We haven't found contact."); + + Assertions.assertThrows(NoSuchContactException.class, () -> cxfClient.getContact("Non existent")); } @Test - public void testBasic() throws NoSuchContactException { + public void testPojoCamelProducer() throws NoSuchContactException { ContactService cxfClient = createCXFClient(); + int countContactsStart = cxfClient.getContacts().getContacts().size(); - cxfClient.addContact(createContact()); - Assertions.assertSame(1, cxfClient.getContacts().getContacts().size(), "We should have one contact."); + RestAssured.given() + .log().all() + .contentType("application/json") + .body(createProducerContact()) + .queryParam(CxfConstants.OPERATION_NAME, "addContact") + .post("/producer/contact") + .then() + .statusCode(200); - Assertions.assertNotNull(cxfClient.getContact("Croway"), "We haven't found contact."); + RestAssured.given() + .contentType("application/json") + .queryParam(CxfConstants.OPERATION_NAME, "getContacts") + .get("/producer/contacts") + .then() + .statusCode(200) + .body("contacts.size()", Matchers.equalTo(countContactsStart + 1)); - Assertions.assertThrows(NoSuchContactException.class, () -> cxfClient.getContact("Non existent")); + Assertions.assertNotNull(cxfClient.getContact("Lukas"), "We haven't found contact."); } } diff --git a/cxf-soap/src/test/java/org/acme/cxf/soap/PojoClientTestIT.java b/cxf-soap/src/test/java/org/acme/cxf/soap/PojoTestIT.java similarity index 94% rename from cxf-soap/src/test/java/org/acme/cxf/soap/PojoClientTestIT.java rename to cxf-soap/src/test/java/org/acme/cxf/soap/PojoTestIT.java index c57df864..e9e3f210 100644 --- a/cxf-soap/src/test/java/org/acme/cxf/soap/PojoClientTestIT.java +++ b/cxf-soap/src/test/java/org/acme/cxf/soap/PojoTestIT.java @@ -19,5 +19,5 @@ package org.acme.cxf.soap; import io.quarkus.test.junit.QuarkusIntegrationTest; @QuarkusIntegrationTest -public class PojoClientTestIT extends PojoClientTest { +public class PojoTestIT extends PojoTest { } diff --git a/cxf-soap/src/test/java/org/acme/cxf/soap/WsdlClientTest.java b/cxf-soap/src/test/java/org/acme/cxf/soap/WsdlClientTest.java index 78eafd99..6f4a7f77 100644 --- a/cxf-soap/src/test/java/org/acme/cxf/soap/WsdlClientTest.java +++ b/cxf-soap/src/test/java/org/acme/cxf/soap/WsdlClientTest.java @@ -25,6 +25,7 @@ import com.example.customerservice.CustomerService; import com.example.customerservice.NoSuchCustomerException; import io.quarkus.test.junit.QuarkusTest; import jakarta.xml.ws.soap.SOAPFaultException; +import org.acme.cxf.soap.utils.CxfServerUtils; import org.apache.cxf.ext.logging.LoggingFeature; import org.apache.cxf.frontend.ClientProxyFactoryBean; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; @@ -37,12 +38,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @QuarkusTest -public class WsdlClientTest extends BaseTest { +public class WsdlClientTest { CustomerService cxfClient; protected CustomerService createCustomerClient() { - String URL = getServerUrl() + "/cxf/services/customer"; + String URL = CxfServerUtils.getServerUrl() + "/customer"; ClientProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(CustomerService.class);
