Consuming a SOAP service with Apache Camel

When was the last time you worked with a SOAP service? For me, it was actually yesterday.

SOAP has been around for a long time. You might think that it’s had its day, but it doesn’t seem to be disappearing yet.

I see SOAP services still in use at many companies. Some well-known enterprise apps still implement their API using SOAP (here’s looking at you, Workday! 👋).

But the problem with SOAP is that it is unbelievably complicated, and this complexity is unfortunately also reflected in the tools we use to work with them.

With that in mind, here’s a brief guide to invoking SOAP services in Camel, and the basics you need to know to get up and running.

Which component to use?

In Camel there are two main components you can use to work with SOAP services:

  • CXF component (camel-cxf) - This is a Camel wrapper for Apache CXF, a Java library for working with web services. The CXF component in Camel makes it easier to use CXF in your Camel routes. For example, you can use the CXF component to send and receive messages to/from a SOAP service.

  • SOAP dataformat (camel-soap) - This is a Camel DataFormat, so it’s designed for reading and writing SOAP messages only; it doesn’t actually send requests. You might use this dataformat if you want to create a SOAP message (in XML), but send it over a different protocol (like JMS), or write it to disk.

Since I want to invoke a SOAP service (at a SOAP endpoint), I’m going to use the CXF component in this article. So let’s go.

Introducing CXF

Apache CXF is a Java framework for working with web services. It primarily supports SOAP, but can also be used to access other types of services, such as REST and even CORBA.

Apache CXF website home page

You can download Apache CXF from the official web site. But, when you use CXF in Camel, you only need to pull in the Maven dependency below, and CXF will be downloaded as a transitive dependency to your project:

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

CXF can be a complicated beast; but that’s because it has to support a lot of the advanced features of SOAP. Also, the terminology can be really confusing for beginners.

The best thing I’ve learned about working with CXF, is to just start very simple. Get a simple example up and running, and then add all the bells and whistles.

Get the example code on GitHub

Invoking a SOAP service in Camel

Like most things in Java, there’s lots of different ways to invoke a SOAP service in Camel. But when I’m doing it in my projects, I do these 3 things:

  1. Create client classes from the WSDL. This is essential if you want to be able to work with the SOAP messages as plain old Java objects (POJOs). Fortunately there’s a plugin to do this for you - to generate JAXB classes from your WSDL.

  2. Create your request payload. You can do this by creating a Java object from the classes you created in step 1, and manipulate the data using getters and setters.

  3. Set up the CXF (SOAP) endpoint in Camel, and invoke it from your route. This is where you implement your logic in Camel to send your SOAP message.

So here’s how to invoke a SOAP service from Camel using the CXF component:

  1. Add camel-cxf as a dependency in your project.

  2. Use the cxf-codegen-plugin for Maven to create Java classes from your WSDL file for you.

    Get a hold of the WSDL file from the web service, and copy it into a directory src/main/resources/wsdl.

    Then, set up the cxf-codegen-plugin in your Maven pom.xml, like this:

     <plugin>
       <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-codegen-plugin</artifactId>
       <version>3.2.4</version>
       <executions>
         <execution>
           <id>generate-sources</id>
           <phase>generate-sources</phase>
           <configuration>
             <wsdlOptions>
               <wsdlOption>
                 <wsdl>src/main/resources/wsdl/BookService.wsdl</wsdl>
               </wsdlOption>
             </wsdlOptions>
           </configuration>
           <goals>
             <goal>wsdl2java</goal>
           </goals>
         </execution>
       </executions>
     </plugin>
    

    This plugin integrates itself into your Maven build process, so whenever you run a Maven build of your application, it will create and compile Java classes from the WSDL file.

    Alternatively, to generate the classes whenever you want, type mvn generate-sources. The default location for the generated source files is in target/generated-sources/cxf.

  3. Write a Java method to build your request message.

    Since Camel works well with Java beans, it makes sense for us to write a simple Java method that creates a new Object using the classes created by the cxf-codegen-plugin.

    You should start by creating a Java object of the class that represents your service’s request type, e.g. GetBookRequestType, or AddBookType.

    Then it’s a simple case of writing a Java class, with a single method to create and return an object of that type:

     public class GetBookRequestBuilder {
    
         public GetBook getBook(String id) {
             GetBook request = new GetBook();
             request.setID(id);
    
             return request;
         }
     }
    

    Now that this class is written, it can be called from a Camel route.

  4. Invoke your Java bean from a Camel route, to create your request message.

     from("direct:start")
         .setBody(constant("12345"))
         .bean(GetBookRequestBuilder.class)
         // rest of the route goes here...
    

    This code first sets the message body to 12345. Then, when the GetBookRequestBuilder bean is invoked, Camel uses bean parameter binding to pass the value 12345 as the argument to the build(String id) method.

    The method is invoked, returning an Object of type GetBook, for a customer ID of 12345.

  5. Finally, invoke the SOAP service using the CXF component.

    You first need to configure which SOAP operation you’re invoking, by setting the value in a Header:

     .setHeader("operationName", constant("GetBook"))
    

    Then, you can use the CXF component using the cxf:// endpoint, like this:

     .to("cxf://http://localhost:8423/test/BookService"
         + "?serviceClass=com.cleverbuilder.bookservice.BookService"
         + "&wsdlURL=/wsdl/BookService.wsdl")
    

    Where serviceClass is the class that the cxf-codegen-plugin created that represents your SOAP service, and wsdlURL is the path to your WSDL.

Example

If you’ve followed the steps above, your final Camel route should look something like this:

from("direct:start")
    .setBody(constant("12345"))
    .bean(GetBookRequestBuilder.class)

    .setHeader(CxfConstants.OPERATION_NAME,
        constant("GetBook"))
    .setHeader(CxfConstants.OPERATION_NAMESPACE,
        constant("http://www.cleverbuilder.com/BookService/"))

    // Invoke our test service using CXF
    .to("cxf://http://localhost:8423/test/BookService"
        + "?serviceClass=com.cleverbuilder.bookservice.BookService"
        + "&wsdlURL=/wsdl/BookService.wsdl")

    // You can retrieve fields from the response using the Simple language
    .log("The title is: ${body[0].book.title}");

Full working demo

See a demo Camel route which invokes a SOAP service using a WSDL. Click on the button below to see a full example:

Get the example code on GitHub

Got feedback about this article, or questions about working with SOAP services in Camel? Post them in the comments below! Cheers!