“InFuse” a Spring-Boot service with SOAP

Martien van den Akker
virtualsciences
Published in
9 min readJan 14, 2021

Introduction

In this era of Containerization and Microservices you may focus your service development around REST. However, in application integration one protocol still stands firm: SOAP. If you want to replace existing service integrations you’ll have to deal with SOAP.

Red Hat Fuse is a cloud-native integration platform. It’s based on Camel that in itself comes with tons of components. Being quite experienced in Enterprise Service Bus implementations, I tend to investigate the stretch on how to use Red Hat Fuse with Camel as declarative as possible. That means as little Java as possible, and mainly (XML) configuration.

With that in mind I tried to create SOAP Services in Red Hat Fuse, which can call other SOAP Services (in an orchestration manner) with Spring Boot, and XML DSL. And that with as little Java as possible. This is where this article and subsequent ones are about. Let’s get to it.

Implement a Spring Boot SOAP Service with camel-cxf

Create a new Fuse Integration project

Create New Fuse Integration Project

In my previous article I explained how I created a VM with Code Ready Studio. You can manually install it on your Windows box of course, or use your own favorite IDE of choice.

In your Code Ready Studio Workspace Create a new Fuse Integration project, see this figure.

Provide a proper name and location

Give your project a proper name. I don’t like to have my projects in my Eclipse workspace. I tend to put my projects in separate locations that I find easier to find, navigate to and commit in version control.

Choose runtime environment

Select the target environment. You get to choose between Kubernetes/OpenShift and Standalone as a deployment platform. Choose Kubernetes/OpenShift to prepare for deployment on a container platform, and choose Spring Boot as runtime. Since I use Code Ready Studio 12.17, I get to use Camel version 2.23.x. Finish the wizard, which results in a simple Spring Boot application that is triggered by a timer.

Implement a WSDL

The strength of SOAP is that the service is clearly described in a service contract in the form of a WSDL. I haven’t found a good open-source alternative. I must admit that I designed mine with Oracle SOA Quickstart (JDeveloper), that has very proper WSDL and XSD Designers. For this demo you can use the ones in my example project for this article. Use the AnimalOrderService.wsdl that I posted in my Github demo project. Put it in the src/main/resources/wsdl folder (or just check out or download the complete demo project). It expects the AnimalOrders.xsd in de src/main/resources/schema folder.

Dependencies

The following depencies are necessary for using camel-cxf in context of Spring Boot:

pom.xml depencencies snippet

The application.properties file that is generated in the process of creating your Spring Boot service contains several properties that conflict with the embedded Tomcat server that is used in starting up the service. Clean it up to only contain the following:

application.properties

Configure the cxf-endpoint

With that in place we can focus on the main parts of this implementation. As a starting point, I found this Apache Camel site with an example. Later in this article I’ll deviate because I found that this will not work exactly as I would expect.

There are several methods to define an cxfEndpoint. You can do it the Java way, what I may demonstrate in a later article. I prefer to do it in an XML definition. This can be done in the same camel-context.xml spring file. However, I prefer to separate this from the camel-context, in a separate Spring XML file. In Eclipse with Fuse tooling the camel-context is interpreted as it is in a specific graphic designer. Having the CXF in a separate Spring beans XML file, the IDE can interpret it as such a file. Therefor I define a camel-cxf.xml file that contains the cxfEndpoint:

camel-cxf.xml

Important are the xmlns:cxf=”http://camel.apache.org/schema/cxf" namespace declaration and the corresponding schema locations in the xsi:schemaLocation attribute: “… http://camel.apache.org/schema/cxf https://camel.apache.org/schema/cxf/camel-cxf.xsd …”.

With this CXF-namespace declaration the cxf:cxfEndpoint definition can be made. This contains the following attributes:

  • id: The id of the cxfEndpoint bean, used in the uri attribute of the camel from or to component.
  • xmlns:aowsdl: this is the declaration of the target namespace of the WSDL, to be used as a reference in the endpointName and serviceName attributes. In this example I use aowsdl as an abbreviation. But you can standardize to a simpler tns for example.
  • address: the endpoint URI to which the SOAP service implementation should listen to. In this example it is referenced as a property. This is handy because this attribute can be environment specific. I’ll get to it later.
  • wsdlURL: url to your WSDL. This can be a reference to an online WSDL, for instance a concrete one to your referenced service. In this case I use a relative path from the resources folder, that is on the classpath.
  • endpointName: this references the name-attribute of the wsdl:port element in the WSDL. See the picture below. Use the WSDL-targetNamespace reference in the form of aowsdl:portName (or tns:portName, see remark at the xmlns:aowsdl description above).
  • serviceName: the name-attribute of the wsdl:service element of the WSDL. See the picture below. Use the WSDL-targetNamespace reference in the form of aowsdl:serviceName. (or tns:serviceName, see remark at the xmlns:aowsdl description above)
  • serviceClass: fully qualified name of the class that is used as a webservice implementation. For this class you only need a skeleton. I’ll get to that later.
  • dataFormat property value: PAYLOAD defines that we expect the content of the SOAP Body in the $body variable. See this link on DataFormat, for more information on the other options.

For the endpointName and serviceName attributes refer to this picture to see where to get the information:

Application properties

In the cxfEndpoint I defined the address property as a property. There are different possibilities to define those properties. You can import them from a property file as:

You can also use a set of environment-dependent YAML files. Those are a bit easier to edit, and as I’ll show later can be referenced using a separate parameter in the maven run command.

See the application-dev.yml file and place a copy of it in the resources folder. Or at least in a folder that is on the classpath, but the resources folder will do. It looks like:

Pretty simple. Since this is an endpoint of the service provider part of the service, this endpoint should not contain a host address (http://0.0.0.0/) part. That will be implemented by the embedded Tomcat server.

Implementation Class

The cxfEndpoint expects a class to reference for the webservice implementation. It should look like:

AnimalOrderServiceProvider.java

In my first implementations I used an example as described in the previous mentioned example on the Apache site. However, this explicitly defines a Message mode implementation. Doing so, the message you get in the $body variable is the complete SOAP Envelope. To work with the actual body message you need to process the complete SOAP Envelope. I solved that with a simple XSLT that selects the body content. Sending messages would need you to add a SOAP Envelope and transform that to a CXF SOAPMessage. And then you need several lines of Java. Using the class above, which is simpler, together with the dataFormat property value PAYLOAD in the cxfEndpoint, the CXF framework takes care of that. You will need to do some Java when needing to set SOAP Header elements. But I’ll cover that in a later story.

The methods don’t need an implementation. The invoke method is mandatory, since you implement the Provider interface. Besides the invoke method you also need similar methods for the operations you defined in the WSDL and want to implement or use. In a next story about consuming a SOAP service, I’ll get back to this.

Reference cxfEndpoint in the camel-context.xml

With al this preparation done, we’ll get to the actual implemtation in the camel-context.xml. Here it is:

camel-context.xml

In line 5 you see the reference to the camel-cxf.xml. Again, this is a free tip on how to externalize definitions from the camel-context. I find this handy, especially with bigger camel contexts. I haven’t tried yet, but I assume that this allows you to modularize your camel-contexts similarly in case of a very complex integration. In line 6 you see a reference to a camel-beans.xml. In the same way I also externalized my bean definitions. You could combine the beans and cxfEndpoints in one file of course.

Then line 10 is the actual trick. In the from uri attribute you reference the id of the cxfEndpoint as cxf:bean:cxfEndpoint.id.

Transformation with XSLT

Once the service is called from a service consumer, you’ll get the SOAP Body content as an XML DOM object-tree in the $body variable. Since this is a synchronous request-response service, it expects a response message. The goal of this exercise was to get an integration as declarative as possible. Oracle SOA Quickstart (JDeveloper) has a very good XSL Map designer. I grabbed that tool again to quickly design an XSLT transformation for this purpose. You can find it here. At the top of the file you find a process-instruction block that defines the source and target definitions of this transformation. This is how the XSL Map designer knows how to build up a source and target tree:

AnimalOrderRequestToAnimalOrderResponse.xsl

Camel is perfectly capable of calling this transformation, provided that you don’t use Oracle SOA Suite specific functions. Also xpath 2.0 functions are tricky, since you need a namespace declaration that refers to the particular function implementations. You can specify parameters and using camel message header variables provide those to the xslt. Maybe I’ll cover that in a later story too.

The Camel-in-Action-book is not very positive on XSL, since it is considered complex. But it is a very powerful language that you can grow into. There are loads of examples on the internet. Give it a shot, especially because it enables you to do the transformations in your integration declaratively too. If you like: camel supports xquery too.

Run and test

You can build and run the project with the following command:

mvn spring-boot:run -Dspring.profiles.active=dev

The spring.profiles.active property selects the application-dev.yml file to refer to the properties. From Code Ready Studio you can create a maven build run configuration as follows:

Run configuration

I created a SOAP UI project and added it to Github for this and subsequent demos. After running the service you can test is using an ad-hoc request:

SOAP UI Ad-hoc request

Notice the endpoint here. In the application-dev.yml, the endpoint is defined as: /FuseDemos/AnimalOrderService. The embedded Tomcat adds the services resource to the URI.

Conclusion

With this setup you can implement a SOAP Service that is completely defined in XML. The only java you’ll need is a simple skeleton class as a webservice imlementation. In the demo project, you’ll also find a class that is referenced as a bean/processor, to log header values. Of course that is Java too, and you might need several beans and/or processors to do specific tasks. But in essence no actual Java is needed for the message processing.

--

--

Martien van den Akker
virtualsciences

Technology Architect at Oracle Netherlands. The views expressed on this blog are my own and do not necessarily reflect the views of Oracle