Create SOAP Webservice using Apache CXF, Spring

In this tutorial I will show you how you can publish SOAP based web service using Apache cxf, Spring. I am going to use Spring Boot framework to create this SOAP service application. I am using both gradle and maven as build tools.

SOAP stands for Simple Object Access Protocol that works on various protocol and supports only XML structure for data exchange mechanism.

Prerequisites

Java at least 8, Gradle 6.5.1, Maven 3.6.3, Spring Boot 2.3.3, Apache CXF 3.3.7, Javax Validation 2.0.1, Hibernate Validator 6.5.1

Project Setup

First step to create a project in your favorite IDE or tool. The name of the project is spring-soap-webservice-apache-cxf.

If you are creating gradle based project then you can use below build.gradle script:

buildscript {
	ext {
		springBootVersion = '2.3.3.RELEASE'
	}
	
    repositories {
    	mavenCentral()
    }
    
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}"
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web-services:${springBootVersion}") {
		exclude group: 'org.hibernate.validator', module: 'hibernate-validator'
	}
	implementation 'org.apache.cxf:cxf-spring-boot-starter-jaxws:3.3.7'
	implementation 'javax.validation:validation-api:2.0.1.Final'
	implementation('org.hibernate:hibernate-validator:6.1.5.Final')
}

If you are creating maven based project then you can use below pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-soap-webservice-apache-cxf</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.3.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web-services</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.hibernate.validator</groupId>
					<artifactId>hibernate-validator</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
			<version>3.3.7</version>
		</dependency>
		
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>2.0.1.Final</version>
		</dependency>
		
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>6.1.5.Final</version>
		</dependency>
	</dependencies>

    <build>
        <plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>at least 8</source>
					<target>at least 8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

VO Class

Value object class is simple POJO class for holding data.

package com.roytuts.spring.soap.webservice.apache.cxf.dto;

public class Product {

	private int productId;
	private String productName;
	private String productCatg;

	public Product() {
	}

	public Product(int productId, String productName, String productCatg) {
		this.productId = productId;
		this.productName = productName;
		this.productCatg = productCatg;
	}

	// getters and setters
}

WebService Endpoint Interface

I am creating the web service endpoint interface. In this endpoint interface I have put two web methods – one for fetching all products and another for fetching product for a given product id.

package com.roytuts.spring.soap.webservice.apache.cxf.service;

import java.util.List;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

import com.roytuts.spring.soap.webservice.apache.cxf.dto.Product;

@WebService
public interface ProductService {

	@WebMethod
	public List<Product> getAllProducts();

	@WebMethod
	public Product getProduct(@WebParam(name = "productId") int productId);

}

Endpoint Implementation

The is the implementation class for the above endpoint interface. Notice you need to mention the endpoint interface using endpointInterface of annotation @WebService.

package com.roytuts.spring.soap.webservice.apache.cxf.service;

import java.util.List;

import javax.jws.WebService;

import com.roytuts.spring.soap.webservice.apache.cxf.dao.ProductDao;
import com.roytuts.spring.soap.webservice.apache.cxf.dto.Product;

@WebService(endpointInterface = "com.roytuts.spring.soap.webservice.apache.cxf.service.ProductService", serviceName = "productService")
public class ProductServiceImpl implements ProductService {

	private ProductDao dao;

	public ProductServiceImpl() {
		dao = new ProductDao();
	}

	@Override
	public List<Product> getAllProducts() {
		return dao.getAllProducts();
	}

	@Override
	public Product getProduct(int productId) {
		return dao.getProduct(productId);
	}

}

DAO Class

I am not using any database as a persistent storage so I am creating some dummy data to return the results for products.

package com.roytuts.spring.soap.webservice.apache.cxf.dao;

import java.util.ArrayList;
import java.util.List;

import com.roytuts.spring.soap.webservice.apache.cxf.dto.Product;

public class ProductDao {

	List<Product> productList = new ArrayList<Product>();

	public ProductDao() {
		Product p1 = new Product(101, "Laptop", "Electronics");
		Product p2 = new Product(102, "Bannana", "Fruits");
		Product p3 = new Product(103, "Pencil", "Stationary");
		productList.add(p1);
		productList.add(p2);
		productList.add(p3);
	}

	public Product getProduct(int id) {
		for (Product product : productList) {
			if (product.getProductId() == id) {
				return product;
			}
		}
		return null;
	}

	public List<Product> getAllProducts() {
		return productList;
	}

}

Web Service Configuration

This is the configuration class for web service. I am not using XML based configuration and here is the equivalent Java configuration.

Notice how I have defined beans for CXFServlet and jaxws endpoint.

package com.roytuts.spring.soap.webservice.apache.cxf.config;

import javax.servlet.Servlet;
import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.roytuts.spring.soap.webservice.apache.cxf.service.ProductServiceImpl;

@Configuration
public class Config {

	@Autowired
	private Bus bus;

	@Bean
	public ServletRegistrationBean<Servlet> servletRegistrationBean(ApplicationContext context) {
		return new ServletRegistrationBean<Servlet>(new CXFServlet(), "/api/*");
	}

	@Bean
	public Endpoint productService() {
		EndpointImpl endpoint = new EndpointImpl(bus, new ProductServiceImpl());
		endpoint.publish("/productService");
		return endpoint;
	}

}

Main Class

A class with main method and @SpringBootApplication is enough to deploy the application into embedded Tomcat server.

package com.roytuts.spring.soap.webservice.apache.cxf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringSoapWebServiceApacheCxfApp {

	public static void main(String[] args) {
		SpringApplication.run(SpringSoapWebServiceApacheCxfApp.class, args);
	}

}

Finally you can run the above class and you will be able to access the WSDL at location http://localhost:8080/api/productService?wsdl.

The content of WSDL file is given below:

<?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://service.cxf.apache.webservice.soap.spring.roytuts.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="productService" targetNamespace="http://service.cxf.apache.webservice.soap.spring.roytuts.com/">
  <wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://service.cxf.apache.webservice.soap.spring.roytuts.com/" elementFormDefault="unqualified" targetNamespace="http://service.cxf.apache.webservice.soap.spring.roytuts.com/" version="1.0">

  <xs:element name="getAllProducts" type="tns:getAllProducts"/>

  <xs:element name="getAllProductsResponse" type="tns:getAllProductsResponse"/>

  <xs:element name="getProduct" type="tns:getProduct"/>

  <xs:element name="getProductResponse" type="tns:getProductResponse"/>

  <xs:complexType name="getAllProducts">
    <xs:sequence/>
  </xs:complexType>

  <xs:complexType name="getAllProductsResponse">
    <xs:sequence>
      <xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:product"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="product">
    <xs:sequence>
      <xs:element minOccurs="0" name="productCatg" type="xs:string"/>
      <xs:element name="productId" type="xs:int"/>
      <xs:element minOccurs="0" name="productName" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="getProduct">
    <xs:sequence>
      <xs:element name="productId" type="xs:int"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="getProductResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="tns:product"/>
    </xs:sequence>
  </xs:complexType>

</xs:schema>
  </wsdl:types>
  <wsdl:message name="getAllProducts">
    <wsdl:part element="tns:getAllProducts" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getProduct">
    <wsdl:part element="tns:getProduct" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getProductResponse">
    <wsdl:part element="tns:getProductResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getAllProductsResponse">
    <wsdl:part element="tns:getAllProductsResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="ProductService">
    <wsdl:operation name="getAllProducts">
      <wsdl:input message="tns:getAllProducts" name="getAllProducts">
    </wsdl:input>
      <wsdl:output message="tns:getAllProductsResponse" name="getAllProductsResponse">
    </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getProduct">
      <wsdl:input message="tns:getProduct" name="getProduct">
    </wsdl:input>
      <wsdl:output message="tns:getProductResponse" name="getProductResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="productServiceSoapBinding" type="tns:ProductService">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getAllProducts">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="getAllProducts">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getAllProductsResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getProduct">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="getProduct">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getProductResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="productService">
    <wsdl:port binding="tns:productServiceSoapBinding" name="ProductServiceImplPort">
      <soap:address location="http://localhost:8080/api/productService"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

In next tutorial (https://roytuts.com/consume-soap-webservice-using-apache-cxf-spring/) we will see how to consume this service.

Source Code

Download

Thanks for reading.

2 thoughts on “Create SOAP Webservice using Apache CXF, Spring

  1. Hi
    Thanks for this tutorial. My WSDL file is getting generated and I can see through URL but it is not getting saved in src/main/resources/wsdl folder. can you help me in this issue.

  2. Thanks, very usefull tutorial for me. But I want to configure Apache CFX with Spring using annotations, without xml file spring-beans.xml. Could you please describe this?

Leave a Reply

Your email address will not be published. Required fields are marked *