Popular searches
//

Spring Boot & Apache CXF – How to SOAP in 2016

18.2.2016 | 12 minutes reading time

Even though it looks as though REST killed every SOAP service on the planet, in 2016 there are still costumers who need to build a web service infrastructure from scratch exposing good old SOAP web services. So why not base them on state-of-the-art Spring Boot with zero-XML-configured Apache CXF?

Spring Boot & Apache CXF – Tutorial

Part 1: Spring Boot & Apache CXF – How to SOAP in 2016
Part 2: Spring Boot & Apache CXF – Testing SOAP web services
Part 3: Spring Boot & Apache CXF – XML validation and custom SOAP faults
Part 4: Spring Boot & Apache CXF – Logging & Monitoring with Logback, Elasticsearch, Logstash & Kibana
Part 5: Spring Boot & Apache CXF – SOAP on steroids fueled by cxf-spring-boot-starter

For sure there are trending things that are more likely to earn some appreciation in the coffee kitchen – the projects are there. And there are many more applications that will run these services for years to come. So why not build these on top of some recent technologies requiring far less maintenance? And last but not least – experience shows that a standardised API defintion designed contract first has its benefits. Just look at initatives like JSON-schema.

Ok dude, it´s SOAP. But please, I don´t want to use old technologies!

So it has to be some sort of SOAP web service. Right. But this shouldn’t mean that we have to use ancient technologies and approaches. We want to use some fancy stuff like Spring Boot , which is widely used in microservice projects, or something like log analysis with the Elasticsearch Logstash Kibana (ELK) Stack.

Looking for a good example…

After nearly no time (thanks to the excellent Spring.io-guides ) you´ll have a running example with Spring WS , Spring Boot & its embedded Tomcat that you can start from the command line or with a “Run as…” inside your IDE – especially the guide producing-web-service . Then your SOAP web service is up and running fast and you can test it with the SOAP test client SoapUI . The problem with this first project: This ‘hello world’ level often isn´t sufficient in real-world projects, e.g. looking at the example web service which is defined via a simple XML-schema file. Additionally there´s no WSDL – it´s generated at runtime.

But in bigger projects, the web service definitions are also bigger. Often, there´s a WSDL defining some web service methods and importing lots and lots of XSDs resulting in a broad amount of XML namespace definitions. It seems that currently there´s no open web service (WSDL) out there which uses at least some of the specialities that real “enterprise web services” use (e.g. like the BiPro-Webservices, which are used broadly in the german Insurance-Market). In order to settle this tutorial on a more comparable example, I had to improvise.

In many tutorials that deal with web services, you can find the WeatherWS-Service from CDYNE . I took this freely available service (or more exact it´s WSDL) and added many of the things that you´re confronted within enterprise environments. For example, with lots of XSD imports, more complex request messages, custom exception types and web service methods that return some kind of attachment (like PDFs). More details on that and a description of what the WSDL this tutorial is based on looks like will follow in Step 2…

But why not use SpringWS, but Apache CXF instead?

As the aforementioned “enterprise WSDLs” and their corresponding specifications do make heavy use of a wide variety of WS* standards , the webservice framework of your choice should be capable of handling all of them correctly. In my experience and despite the existence of the standards in extreme situations (that for sure will occur in your project) it is a best practice to use the framework that is most widely adopted on the market and most widely used. And this, sadly, is not SpringWS, although it has the best out-of-the-box integration in Spring. The most widly used web services Framework is Apache CXF . If it doesn´t work with CXF, it often doesn´t work at all.

SOAP without XML/XSLT – What´s all this about?

Ok, at the end of the day, we need some sort of XML inside our framework, finally resulting in some other XML as response. Otherwise, we wouldn´t do SOAP. But does that mean we have to get our hands dirty with those XML technologies? Do we really have to search for and pull out our old dusty XSLT bible? Do we need to compare the pros and cons of those XML parsers again (DOM vs. SAX ) and at the end lose our lovely compiler that cannot check for our typos if the API definition (XML-schema) changes? Somehow we don´t want this any more in 2016.

Additionally we got used to JSON working with Jackson and now we simply don´t want to give up this comfort. And although Apache CXF is simply the best framework in that field, we don´t want to accept Spring Beans that need to be defined with Spring´s XML configuration – as most of the CXF docs shows. But could this work out in the end? Yes, it works very well. And we will see it, step by step. Just read on and get your hands dirty.

Step1: Let´s do it…

The following steps are fully reproduced from the project step1_simple_springboot_app_with_cxf , which along with all other tutorial steps can be found in the GitHub repository tutorial-soap-spring-boot-cxf .

Bringing Spring Boot & Apache CXF up and running

Our first goal should be to get Spring Boot up together with Apache CXF. As a starting point, I love to use the Spring Initializr . Just choose “Web” and optionally “DevTools”. After importing the resulting project into our IDE, we have to add the correct dependency for Apache CXF. If you use Maven as I do, I added the dependencies cxfrtfrontendjaxws and cxfrt-transports-http along with the current CXF version 3.1.4 to my pom.xml. After our build tool has imported both libraries and some dependencies, we can add two spring beans to our ***Application.java, which will initialize CXF completely:

1@SpringBootApplication
2public class SimpleBootCxfApplication {
3 
4    public static void main(String[] args) {
5    SpringApplication.run(SimpleBootCxfApplication.class, args);
6    }
7 
8    @Bean
9    public ServletRegistrationBean dispatcherServlet() {
10        return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
11    }
12 
13    @Bean(name=Bus.DEFAULT_BUS_ID)
14    public SpringBus springBus() {      
15        return new SpringBus();
16    }
17}

The CXFServlet will process all SOAP requests that reach our URI /soap-api/* and the cxf-SpringBus gets the CXF framework up and running, with all needed CXF modules – see CXF´s architecture . As soon as we start our ***Application.java (simple “Run as…” is enough), Spring Boot initializes its embedded Tomcat, registers the CXFServlet, and we can type the following URL into our Browser http://localhost:8080/soap-api . We´ve done well if CXF says:

No services have been found.

…as there are no services deployed until now 🙂

Step2: From WSDL to Java…

To reach our “no XML” goal, we could use a XML databinding framework such as Java Architecture for XML Binding (JAXB) . In combination with the “Java API for XML Web Services” (JAX-WS ) we have a comfortable chance to provide SOAP web services with Java standard tools – the reference implementation (RI) is part of the Java runtime and can be used out-of-the-box.

There´s no free lunch … erm … example

Again everything will be reproducable, as we extend our example from step 1. The running example sources can be found in the project step2_wsdl_2_java_maven .

The structure of the mentioned web service example http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL is not comparable with our Eénterprise WSDLs out there. As I said, I extended this example until it was more comparable with the bigger WSDLs, especially thinking about “the how” – not really the actual size. The full example WSDL with all imported XML-schema files is also available on GitHub .

If you can hardly remember what this WSDL thingy was… Just remember one thing: read it from bottom to the top. 🙂

Throw out unnecessary things…

Our example WeatherService has many wsdl:ports that connect to their own wsdl:binding each, which leads to unnecessary complexity. So in our derived web service there´s only one wsdl:port:

1<wsdl:service name="Weather">
2    <wsdl:port name="WeatherService" binding="weather:WeatherService">
3        <soap:address location="http://localhost:8095/soap-api/WeatherSoapService_1.0"/>
4    </wsdl:port>
5</wsdl:service>

This implies that while we have three web service methods, they are just defined once – and not repeated three times because of the many ports:

1<wsdl:operation name=“GetWeatherInformation“></wsdl:operation>
2<wsdl:operation name=“GetCityForecastByZIP“></wsdl:operation>
3<wsdl:operation name=“GetCityWeatherByZIP“></wsdl:operation>

If you look into the GitHub repository, you may recognize a custom exception type – a usual practice of those bigger enterprise WSDLs. We will see how to deal with that one in a further step .

The wsdl:portType finally defines what the (XML) requests and reponses of our web service methods will look like – and how they will act in error situations.

Nested XSD imports…

Following the definition of the wsdl:messages element, the fragments of the XML schema are referenced. Here´s the biggest difference between our derived example and the original WeatherService:

Our WSDL imports the central Weather1.0.xsd , which again imports weather-general.xsd and weather-exception.xsd .

And there are more imports in those XSDs. The effort was necessary to emulate the considerably bigger and more complex web services that are used in the field out there. Not really reaching that size, our service helps us show many techniques that matter to get things working. I was really anxious if my choosen toolchain could handle that WSDL. It wasn´t really a problem. We´ll see it step by step.

WSDL-2-Java (finally!)

Because our WSDL describes our web service API contract-first, our dependent Java classes should always represent the current state of the WSDL. It should therefore regularly be generated from it. Furthermore, as our WSDL describes all aspects of our API, we don´t want to check in those generated Java classes into our version control system.

These requirements are easily implemented using a Maven plugin which will generate all necessary bindings and classes in the generate-sources phase, which includes all the technical and the functional classes our web service needs to live.

If you have a look at the already recommended getting started guides , the jaxb2-maven-plugin is used in most of them. If you look a bit further, you´ll find lots of plugins and corresponding discussions, which one is the best . But because we decided to use JAX-WS, the usage of the Maven plugin of the JAX-WS-commons project seems to be a good choice.

But be careful: The JAX WS Maven plugin is back under mojohaus goverance. You can track the development progress on Github . Because of this we´ll use the more recent groupId org.codehaus.mojo instead of org.jvnet.jax-ws-commons in our Maven poms.

Configuring the Maven plugin

The configuration of the jaxwsMavenplugin shouldn´t be underestimated. So let´s look at the build section of our pom :

1<plugin>
2    <groupId>org.codehaus.mojo</groupId>
3    <artifactId>jaxws-maven-plugin</artifactId>
4    <version>2.4.1</version>
5    <configuration>...</configuration>
6</plugin>

Starting from the -tag it´s getting interesting:

1<configuration>
2    <wsdlUrls>
3        <wsdlUrl>src/main/resources/service-api-definition/Weather1.0.wsdl</wsdlUrl>
4    </wsdlUrls>
5    <sourceDestDir>target/generated-sources/wsdlimport/Weather1.0</sourceDestDir>
6    <vmArgs>
7        <vmArg>-Djavax.xml.accessExternalSchema=all</vmArg>
8    </vmArgs>
9</configuration>

The  defines where our WSDL resides as resource and the  decides where to put the generated Java classes. Because we´ve chosen a realistic example, this configuration wouldn’t work for our WSDL with this bunch of imported and nested XSDs. So we have to add a : -Djavax.xml.accessExternalSchema=all makes sure that no XML schema is forgotten.

After the necessary definition of the Maven goal wsimport, we use a second plugin: the build-helper-maven-plugin to add the generated Java classes to our classpath. Now we can use them like any other class in our project. If you want to give it a try, just run

1mvn clean generate-sources

on commandline after you got the project from step2_wsdl_2_java_maven . This should generate all necessary classes into the folder target/generated-sources/wsdlimport/Weather1.0. If you inspect the result, you should recognize the similarity between the package-structure and how the sample-XSDs are structured.

Finally don´t forget to prevent the generated Java classes from beeing checked in into your version control system, as we don´t want to have them there. If you use Git, you can simply put the /target-Folder into your .gitignore – if it´s not already there.

Step3: a running SOAP-Endpoint

This next step will finally bring our first SOAP end point to life. So let´s extend our project from step2. Again the complete code can be found on GitHub in step3_jaxws-endpoint-cxf-spring-boot .

As we now begin to extend our configuration, we should grant our project its own @Configuration-annotated Class. There we´ll initialize CXF and our first end point. As a consequence, our Application class is reduced to the minimum necessary to fire up Spring Boot. Additionally even with SpringBoot we can use a @ComponentScan to fasten scanning of Spring beans and components.

Again we see the beans SpringBus and ServletRegistrationBean inside our @Configuration-Class. To configure the Endpoint, we need two additional beans. Let´s start by defining the service end point interface (SEI):

1@Bean
2public WeatherService weatherService() {
3    return new WeatherServiceEndpoint();
4}

The SEI implementing class WeatherServiceEndpoint is not generated and we have to create one manually. This class represents the place where the functional implementation begins. But within this step we only have to create this class so that we can instantiate it inside our bean defintion.

The second bean to define is javax.xml.ws.Endpoint. This is the point where the Apache CXF docs get really annoying because there´s not really a description to define all necessary beans without XML. But hey, this is where this tutorial comes in handy. 🙂

The crucial point is to return an instance of org.apache.cxf.jaxws.EndpointImpl, which we forward to the SpringBus and our WeatherServiceEndpoint via constructor-arg:

1@Bean
2public Endpoint endpoint() {
3    EndpointImpl endpoint = new EndpointImpl(springBus(), weatherService());
4    endpoint.publish("/WeatherSoapService_1.0");
5    endpoint.setWsdlLocation("Weather1.0.wsdl");
6    return endpoint;
7}

Furthermore we have to use the .publish-Method of our org.apache.cxf.jaxws.EndpointImpl to define the last part of our WebService-URI.

If you now fire up our application, as you´re used to with SpringBoot, a browser should show our WeatherService beneath “Available SOAP services”, when we point it to http://localhost:8080/soap-api/ – including all three available web service methods.

As part of the next step , we´ll see how we can call our web service from within a unit or integration test. At this current step, a test call with SoapUI should do. If you start SoapUI and paste our WSDLs URI into the corresponding field inside “New SOAP Project”, everything necessary should be generated to start a real SOAP request against our end point. If you give it a try, you´ll notice an error-free response that doesn´t contain much for the moment.

So finally, our first SOAP end point with SpringBoot, Apache CXF and JAX-WS is up and running. Nice 🙂 But in the next part(s) of this tutorial we still have plenty to do. We´ll see how to test a SOAP web service from within a unit or integration test. We will beautify our responses´ namespace prefixes and customize our SOAP faults so they validate against a predefined XML-schema – even if there´s something send to our end point that´s not even XML or at least not valid in terms of our XML schemas.

Additionally we´ll see how we could use a small ELK Stack to watch the incoming SOAP-Requests on our server. And experience shows that you´ll need some kind of functional validation of your data which goes beyond validation of XSDs, e.g. to call backends, to gather the necessary data to answer the request accordingly. After several experiments with BeanValidation and other things, I can give you a smart recommendation for handling more complex functional validation: the new OMG-standard DMN  implemented by camunda´s DMN-Engine .

share post

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.