Spring Boot SOAP Web Service Example

By Arvind Rai, October 21, 2021
This page will walk through Spring Boot SOAP web Service example. Here we will create SOAP web service producer and consumer for CRUD operations. For producer, we need to create XML schema to create WSDL. For WSDL we are configuring DefaultWsdl11Definition using JavaConfig. The producer configuration class is annotated with @EnableWs and extends WsConfigurerAdapter. SOAP web service Endpoints are created using Spring annotations such as @Endpoint, @PayloadRoot and @ResponsePayload. To handle database operations we are using JPA CrudRepository.
In SOAP web service client application, we need to generate Java source code using WSDL produced by SOAP web service producer. We need to create a service class extending WebServiceGatewaySupport that provides WebServiceTemplate to send request and receive response. To serialize and deserialize XML requests we need to configure Jaxb2Marshaller.
Now find the complete example of SOAP web service producer and consumer using Spring Boot step-by-step.

1. Technologies Used

Find the technologies being used in our example.
1. Java 16
2. Spring 5.3.10
3. Spring Boot 2.5.5
4. MojoHaus JAXB2 Maven Plugin 2.5.0
5. WSDL4J 1.6.3
6. JVNET JAXB2 Maven Plugin 0.14.0
7. Maven 3.8.1
8. MySQL 5.5

2. Producing SOAP Web Service for CRUD

We will create a web service producer application. We will perform CRUD operations on articles. We need to create an XML schema in which we will define XML request and XML response for create, read, update and delete operations. Application will produce WSDL on the basis of defined XML schema. We will test our web service producer using web service client application as well as using SOAP UI. Now find the complete web service producer example step by step.

2.1 Project Structure in Eclipse

Find the project structure in Eclipse for web service producer.
Spring Boot SOAP Web Service Example

2.2 Create Maven File

Find the pom.xml to produce SOAP web service.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.concretepage</groupId>
	<artifactId>soap-ws</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>spring-demo</name>
	<description>Spring SOAP WS</description>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.5</version>
		<relativePath />
	</parent>
	<properties>
		<java.version>16</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web-services</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>6.0.5</version>
		</dependency>
		<dependency>
			<groupId>wsdl4j</groupId>
			<artifactId>wsdl4j</artifactId>
			<version>1.6.3</version>
		</dependency>
		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>jaxb2-maven-plugin</artifactId>
				<version>2.5.0</version>
				<executions>
					<execution>
						<id>xjc-schema</id>
						<goals>
							<goal>xjc</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<sources>
						<source>src/main/resources/xsds</source>
					</sources>
					<packageName>com.concretepage.gs_ws</packageName>
					<clearOutputDir>false</clearOutputDir>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project> 
spring-boot-starter-web-services: Spring Boot starter for Spring web services.
wsdl4j: It allows to create, represent and manipulate WSDL documents.
jaxb2-maven-plugin: It generates Java classes from XML schemas.

2.3 Create XML Schema for CRUD Operation

We will create XML schema (XSD) to define web service domains. Spring web service will export XSD as WSDL automatically. In our example we are creating XML schema for CRUD operations.
articles.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.concretepage.com/article-ws"
           targetNamespace="http://www.concretepage.com/article-ws" elementFormDefault="qualified">

    <xs:element name="getArticleByIdRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="articleId" type="xs:long"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="getArticleByIdResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="articleInfo" type="tns:articleInfo"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="articleInfo">
        <xs:sequence>
            <xs:element name="articleId" type="xs:long"/>
            <xs:element name="title" type="xs:string"/>
            <xs:element name="category" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:element name="getAllArticlesRequest">
        <xs:complexType/>
    </xs:element>    
    <xs:element name="getAllArticlesResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="articleInfo" type="tns:articleInfo" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>   
    <xs:complexType name="serviceStatus">
        <xs:sequence>
            <xs:element name="statusCode" type="xs:string"/>
            <xs:element name="message" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>     
    <xs:element name="addArticleRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="title" type="xs:string"/>
                <xs:element name="category" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="addArticleResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="serviceStatus" type="tns:serviceStatus"/>            
                <xs:element name="articleInfo" type="tns:articleInfo"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="updateArticleRequest">
        <xs:complexType>
            <xs:sequence>
               <xs:element name="articleInfo" type="tns:articleInfo"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="updateArticleResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="serviceStatus" type="tns:serviceStatus"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="deleteArticleRequest">
        <xs:complexType>
            <xs:sequence>
               <xs:element name="articleId" type="xs:long"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="deleteArticleResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="serviceStatus" type="tns:serviceStatus"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>                   
</xs:schema> 
We have defined XML schema for request and response to create, read, update and delete articles.

2.4 Generate Domain Classes from XML Schema

We will generate Java classes from XSD file. In our example we have articles.xsd file for CRUD operations. In pom.xml we have configured jaxb2-maven-plugin which role is to generate Java classes from XML schema. jaxb2-maven-plugin has been configured in pom.xml as following.
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>jaxb2-maven-plugin</artifactId>
  <version>2.3.1</version>
  <executions>
     <execution>
	<id>xjc-schema</id>
	<goals>
	  <goal>xjc</goal>
	</goals>
     </execution>
  </executions>
  <configuration>
     <sources>
	<source>src/main/resources/xsds</source>
     </sources>
     <packageName>com.concretepage.gs_ws</packageName>
     <clearOutputDir>false</clearOutputDir>
  </configuration>
</plugin> 
When we run command mvn eclipse:eclipse or mvn clean package then xjc goal will run and that will pick XSD file from src/main/resources/xsds directory and generate domain classes in com.concretepage.gs_ws package under the target/generated-sources/jaxb directory. Find all the generated Java classes for articles.xsd.

AddArticleRequest.java
AddArticleResponse.java
ArticleInfo.java
DeleteArticleRequest.java
DeleteArticleResponse.java
GetAllArticlesRequest.java
GetAllArticlesResponse.java
GetArticleByIdRequest.java
GetArticleByIdResponse.java
ObjectFactory.java
package-info.java
ServiceStatus.java
UpdateArticleRequest.java
UpdateArticleResponse.java

2.5 Configure Web Service Bean

We will create Spring web service Java configuration class annotated with @EnableWs and extending WsConfigurerAdapter. Now we will configure web service DefaultWsdl11Definition bean as following.
WSConfig.java
package com.concretepage.config;
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 org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;

@Configuration
@EnableWs
public class WSConfig extends WsConfigurerAdapter {
	@Bean
	public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
		MessageDispatcherServlet servlet = new MessageDispatcherServlet();
		servlet.setApplicationContext(applicationContext);
		servlet.setTransformWsdlLocations(true);
		return new ServletRegistrationBean(servlet, "/soapws/*");
	}
	@Bean(name = "articles")
	public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema articlesSchema) {
		DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
		wsdl11Definition.setPortTypeName("ArticlesPort");
		wsdl11Definition.setLocationUri("/soapws");
		wsdl11Definition.setTargetNamespace("http://www.concretepage.com/article-ws");
		wsdl11Definition.setSchema(articlesSchema);
		return wsdl11Definition;
	}
	@Bean
	public XsdSchema articlesSchema() {
		return new SimpleXsdSchema(new ClassPathResource("xsds/articles.xsd"));
	}
} 
DefaultWsdl11Definition is configuring WSDL definitions such as port type name, location URI, target namespace, schema etc.
XsdSchema represents an abstraction for XSD schemas.
ServletRegistrationBean configures application context, URL mappings etc.
@EnableWs is used with @Configuration to have Spring web services defined in WsConfigurerAdapter.

2.6 Create Web Service Endpoint for CRUD Operation

We will create web service Endpoint for CRUD operations. Endpoint classes accept web service requests and return web service responses. Here we need to use Java classes generated by XSD files for request and response.
ArticleEndpoint.java
package com.concretepage.endpoints;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import com.concretepage.entity.Article;
import com.concretepage.gs_ws.AddArticleRequest;
import com.concretepage.gs_ws.AddArticleResponse;
import com.concretepage.gs_ws.ArticleInfo;
import com.concretepage.gs_ws.DeleteArticleRequest;
import com.concretepage.gs_ws.DeleteArticleResponse;
import com.concretepage.gs_ws.GetAllArticlesResponse;
import com.concretepage.gs_ws.GetArticleByIdRequest;
import com.concretepage.gs_ws.GetArticleByIdResponse;
import com.concretepage.gs_ws.ServiceStatus;
import com.concretepage.gs_ws.UpdateArticleRequest;
import com.concretepage.gs_ws.UpdateArticleResponse;
import com.concretepage.service.IArticleService;

@Endpoint
public class ArticleEndpoint {
	private static final String NAMESPACE_URI = "http://www.concretepage.com/article-ws";
	@Autowired
	private IArticleService articleService;	

	@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getArticleByIdRequest")
	@ResponsePayload
	public GetArticleByIdResponse getArticle(@RequestPayload GetArticleByIdRequest request) {
		GetArticleByIdResponse response = new GetArticleByIdResponse();
		ArticleInfo articleInfo = new ArticleInfo();
		BeanUtils.copyProperties(articleService.getArticleById(request.getArticleId()), articleInfo);
		response.setArticleInfo(articleInfo);
		return response;
	}
	@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getAllArticlesRequest")
	@ResponsePayload
	public GetAllArticlesResponse getAllArticles() {
		GetAllArticlesResponse response = new GetAllArticlesResponse();
		List<ArticleInfo> articleInfoList = new ArrayList<>();
		List<Article> articleList = articleService.getAllArticles();
		for (int i = 0; i < articleList.size(); i++) {
		     ArticleInfo ob = new ArticleInfo();
		     BeanUtils.copyProperties(articleList.get(i), ob);
		     articleInfoList.add(ob);    
		}
		response.getArticleInfo().addAll(articleInfoList);
		return response;
	}	
	@PayloadRoot(namespace = NAMESPACE_URI, localPart = "addArticleRequest")
	@ResponsePayload
	public AddArticleResponse addArticle(@RequestPayload AddArticleRequest request) {
		AddArticleResponse response = new AddArticleResponse();		
    	        ServiceStatus serviceStatus = new ServiceStatus();		
		Article article = new Article();
		article.setTitle(request.getTitle());
		article.setCategory(request.getCategory());		
                boolean flag = articleService.addArticle(article);
                if (flag == false) {
        	   serviceStatus.setStatusCode("CONFLICT");
        	   serviceStatus.setMessage("Content Already Available");
        	   response.setServiceStatus(serviceStatus);
                } else {
		   ArticleInfo articleInfo = new ArticleInfo();
	           BeanUtils.copyProperties(article, articleInfo);
		   response.setArticleInfo(articleInfo);
        	   serviceStatus.setStatusCode("SUCCESS");
        	   serviceStatus.setMessage("Content Added Successfully");
        	   response.setServiceStatus(serviceStatus);
                }
                return response;
	}
	@PayloadRoot(namespace = NAMESPACE_URI, localPart = "updateArticleRequest")
	@ResponsePayload
	public UpdateArticleResponse updateArticle(@RequestPayload UpdateArticleRequest request) {
		Article article = new Article();
		BeanUtils.copyProperties(request.getArticleInfo(), article);
		articleService.updateArticle(article);
    	        ServiceStatus serviceStatus = new ServiceStatus();
    	        serviceStatus.setStatusCode("SUCCESS");
    	        serviceStatus.setMessage("Content Updated Successfully");
    	        UpdateArticleResponse response = new UpdateArticleResponse();
    	        response.setServiceStatus(serviceStatus);
    	        return response;
	}
	@PayloadRoot(namespace = NAMESPACE_URI, localPart = "deleteArticleRequest")
	@ResponsePayload
	public DeleteArticleResponse deleteArticle(@RequestPayload DeleteArticleRequest request) {
		Article article = articleService.getArticleById(request.getArticleId());
    	        ServiceStatus serviceStatus = new ServiceStatus();
		if (article == null ) {
	    	    serviceStatus.setStatusCode("FAIL");
	    	    serviceStatus.setMessage("Content Not Available");
		} else {
		    articleService.deleteArticle(article);
	    	    serviceStatus.setStatusCode("SUCCESS");
	    	    serviceStatus.setMessage("Content Deleted Successfully");
		}
    	        DeleteArticleResponse response = new DeleteArticleResponse();
    	        response.setServiceStatus(serviceStatus);
		return response;
	}	
} 
Find the Spring web service annotations used to create Endpoint.

@Endpoint: The class annotated with @Endpoint becomes web service Endpoint.

@PayloadRoot: The method annotated with @PayloadRoot becomes an Endpoint method that accepts incoming request and returns response. It has attributes localPart and namespace. The attribute localPart is required and it signifies the local part of payload root element. The attribute namespace is optional and it signifies the namespace of payload root element.

@ResponsePayload: The annotation @ResponsePayload bounds the response of the method to response payload.

2.7 Create Database Table

Find the MySQL database table used in our example.
Database Table
CREATE DATABASE IF NOT EXISTS `concretepage`;
USE `concretepage`;

CREATE TABLE IF NOT EXISTS `articles` (
  `article_id` bigint(5) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `category` varchar(100) NOT NULL,
  PRIMARY KEY (`article_id`)
) ENGINE=InnoDB;

INSERT INTO `articles` (`article_id`, `title`, `category`) VALUES
	(1, 'Java Concurrency', 'Java'),
	(2, 'Spring Boot Getting Started', 'Spring Boot'); 

2.8 Spring Boot application.properties

In Spring Boot application we need to configure datasource, JPA properties and logging etc in application.properties file located in the classpath. These properties will automatically be read by Spring boot.
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/concretepage
spring.datasource.username=root
spring.datasource.password=cp
spring.datasource.tomcat.max-wait=20000
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.max-idle=20
spring.datasource.tomcat.min-idle=15

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.id.new_generator_mappings = false
spring.jpa.properties.hibernate.format_sql = true

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE 

2.9 Create Service for CRUD using JPA CrudRepository

We will create a service class for CRUD operations using CrudRepository implementation. CrudRepository provides generic CRUD operations on a repository for a specific type. CrudRepository is a Spring data interface and to use it we need to create our interface by extending CrudRepository. Spring provides CrudRepository implementation class automatically at runtime. It contains methods such as save, findById, delete, count etc. We can also create custom methods. We can start our custom query method names with find...By, read...By, query...By, count...By, and get...By. Before By we can add expression such as Distinct. After By we need to add property names of our entity. We can also use @Query annotation to create custom methods.
Now find the repository interface used in our example.
ArticleRepository.java
package com.concretepage.repository;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import com.concretepage.entity.Article;

public interface ArticleRepository extends CrudRepository<Article, Long>  {
	Article findByArticleId(long articleId);
        List<Article> findByTitleAndCategory(String title, String category);
} 
The implementation class of our repository interface will be created by Spring automatically. Now we will create service and use or repository for CRUD operations.
IArticleService.java
package com.concretepage.service;
import java.util.List;
import com.concretepage.entity.Article;

public interface IArticleService {
     List<Article> getAllArticles();
     Article getArticleById(long articleId);
     boolean addArticle(Article article);
     void updateArticle(Article article);
     void deleteArticle(Article article);
} 
ArticleService.java
package com.concretepage.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.concretepage.entity.Article;
import com.concretepage.repository.ArticleRepository;

@Service
public class ArticleService implements IArticleService {
	@Autowired
	private ArticleRepository articleRepository;

	@Override
	public Article getArticleById(long articleId) {
		Article obj = articleRepository.findByArticleId(articleId);
		return obj;
	}	
	@Override
	public List<Article> getAllArticles(){
		List<Article> list = new ArrayList<>();
		articleRepository.findAll().forEach(e -> list.add(e));
		return list;
	}
	@Override
	public synchronized boolean addArticle(Article article){
	        List<Article> list = articleRepository.findByTitleAndCategory(article.getTitle(), article.getCategory()); 	
                if (list.size() > 0) {
    	           return false;
                } else {
    	           article = articleRepository.save(article);
    	           return true;
                }
	}
	@Override
	public void updateArticle(Article article) {
		articleRepository.save(article);
	}
	@Override
	public void deleteArticle(Article article) {
		articleRepository.delete(article);
	}
} 
Article.java
package com.concretepage.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="articles")
public class Article implements Serializable { 
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="article_id")
        private long articleId;  
	@Column(name="title")
        private String title;
	@Column(name="category")	
	private String category;
	public long getArticleId() {
		return articleId;
	}
	public void setArticleId(long articleId) {
		this.articleId = articleId;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}
} 

2.10 Run SOAP Web Service

Find the class annotated with @SpringBootApplication.
MySpringApplication.java
package com.concretepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MySpringApplication {  
	public static void main(String[] args) {
		SpringApplication.run(MySpringApplication.class, args);
        }       
} 
To test the application, first create table in MySQL as given in the example. Then we can run SOAP web service producer in following ways.
1. Using Maven Command: Download the project source code using the download link given at the end of article. Go to the root folder of the project using command prompt and run the command.
mvn spring-boot:run 
Tomcat server will be started.

2. Using Eclipse: Download the project source code. Import the project into eclipse. Using command prompt, go to the root folder of the project and run.
mvn clean eclipse:eclipse 
and then refresh the project in eclipse. Run Main class MySpringApplication by clicking Run as -> Java Application. Tomcat server will be started.

3. Using Executable JAR: Using command prompt, go to the root folder of the project and run the command.
mvn clean package 
We will get executable JAR soap-ws-0.0.1-SNAPSHOT.jar in target folder. Run this JAR as
java -jar target/soap-ws-0.0.1-SNAPSHOT.jar 
Tomcat server will be started.

Now we are ready to test our SOAP web service producer application. Following URL will run successfully in the browser and return WSDL.
http://localhost:8080/soapws/articles.wsdl 

2.11 Test SOAP Web Service using Soap UI

We will test our SOAP web service producer using SOAP UI. We will use following WSDL URL to create SOAP request.
http://localhost:8080/soapws/articles.wsdl 
Now find the SOAP request and response for CRUD operations.
1. READ
(a). Request: Read article by id
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:art="http://www.concretepage.com/article-ws">
   <soapenv:Header/>
   <soapenv:Body>
      <art:getArticleByIdRequest>
         <art:articleId>2</art:articleId>
      </art:getArticleByIdRequest>
   </soapenv:Body>
</soapenv:Envelope> 
Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:getArticleByIdResponse xmlns:ns2="http://www.concretepage.com/article-ws">
         <ns2:articleInfo>
            <ns2:articleId>2</ns2:articleId>
            <ns2:title>Spring Boot Getting Started</ns2:title>
            <ns2:category>Spring Boot</ns2:category>
         </ns2:articleInfo>
      </ns2:getArticleByIdResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope> 
Find the print screen.
Spring Boot SOAP Web Service Example
(b). Request: Read all articles
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:art="http://www.concretepage.com/article-ws">
   <soapenv:Header/>
   <soapenv:Body>
      <art:getAllArticlesRequest/>
   </soapenv:Body>
</soapenv:Envelope> 
Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:getAllArticlesResponse xmlns:ns2="http://www.concretepage.com/article-ws">
         <ns2:articleInfo>
            <ns2:articleId>1</ns2:articleId>
            <ns2:title>Java Concurrency</ns2:title>
            <ns2:category>Java</ns2:category>
         </ns2:articleInfo>
         <ns2:articleInfo>
            <ns2:articleId>2</ns2:articleId>
            <ns2:title>Spring Boot Getting Started</ns2:title>
            <ns2:category>Spring Boot</ns2:category>
         </ns2:articleInfo>
      </ns2:getAllArticlesResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope> 
2. CREATE
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:art="http://www.concretepage.com/article-ws">
   <soapenv:Header/>
   <soapenv:Body>
      <art:addArticleRequest>
         <art:title>Angular Tutorial</art:title>
         <art:category>Angular</art:category>
      </art:addArticleRequest>
   </soapenv:Body>
</soapenv:Envelope> 
Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:addArticleResponse xmlns:ns2="http://www.concretepage.com/article-ws">
         <ns2:serviceStatus>
            <ns2:statusCode>SUCCESS</ns2:statusCode>
            <ns2:message>Content Added Successfully</ns2:message>
         </ns2:serviceStatus>
         <ns2:articleInfo>
            <ns2:articleId>3</ns2:articleId>
            <ns2:title>Angular Tutorial</ns2:title>
            <ns2:category>Angular</ns2:category>
         </ns2:articleInfo>
      </ns2:addArticleResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope> 
3. UPDATE
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:art="http://www.concretepage.com/article-ws">
   <soapenv:Header/>
   <soapenv:Body>
      <art:updateArticleRequest>
         <art:articleInfo>
            <art:articleId>2</art:articleId>
            <art:title>Update: Spring Boot Getting Started</art:title>
            <art:category>Update: Spring Boot</art:category>
         </art:articleInfo>
      </art:updateArticleRequest>
   </soapenv:Body>
</soapenv:Envelope> 
Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:updateArticleResponse xmlns:ns2="http://www.concretepage.com/article-ws">
         <ns2:serviceStatus>
            <ns2:statusCode>SUCCESS</ns2:statusCode>
            <ns2:message>Content Updated Successfully</ns2:message>
         </ns2:serviceStatus>
      </ns2:updateArticleResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope> 
4. DELETE
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:art="http://www.concretepage.com/article-ws">
   <soapenv:Header/>
   <soapenv:Body>
      <art:deleteArticleRequest>
         <art:articleId>2</art:articleId>
      </art:deleteArticleRequest>
   </soapenv:Body>
</soapenv:Envelope> 
Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:deleteArticleResponse xmlns:ns2="http://www.concretepage.com/article-ws">
         <ns2:serviceStatus>
            <ns2:statusCode>SUCCESS</ns2:statusCode>
            <ns2:message>Content Deleted Successfully</ns2:message>
         </ns2:serviceStatus>
      </ns2:deleteArticleResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope> 

3. Spring SOAP Web Service Client

We will create a Spring SOAP web service client. We need to create Java classes using WSDL provided by Spring web service producer. Spring web service uses Spring OXM module to serialize and deserialize XML requests. We will create service client to perform CRUD operations on articles. We will use Spring Boot to run our SOAP web service client application.

3.1 Project Structure in Eclipse

Find the project structure in Eclipse for SOAP web service client.
Spring Boot SOAP Web Service Example

3.2 Create Maven File

Find the pom.xml for SOAP web service client.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.concretepage</groupId>
	<artifactId>soap-ws-client</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>spring-demo</name>
	<description>Spring SOAP Client</description>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.5</version>
		<relativePath />
	</parent>
	<properties>
		<java.version>16</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-ws-core</artifactId>
		</dependency>
		<dependency>
			<groupId>com.sun.xml.bind</groupId>
			<artifactId>jaxb-impl</artifactId>
			<version>2.3.3</version>
		</dependency>
		<dependency>
			<groupId>javax.xml.soap</groupId>
			<artifactId>javax.xml.soap-api</artifactId>
			<version>1.4.0</version>
		</dependency>
		<dependency>
			<groupId>com.sun.xml.messaging.saaj</groupId>
			<artifactId>saaj-impl</artifactId>
			<version>1.5.2</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.jvnet.jaxb2.maven2</groupId>
				<artifactId>maven-jaxb2-plugin</artifactId>
				<version>0.14.0</version>
				<executions>
					<execution>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<schemaLanguage>WSDL</schemaLanguage>
					<generatePackage>com.concretepage.wsdl</generatePackage>
					<schemas>
						<schema>
							<url>http://localhost:8080/soapws/articles.wsdl</url>
						</schema>
					</schemas>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project> 
spring-ws-core: It resolves Spring web service core dependency.
maven-jaxb2-plugin: It generates Java classes from WSDL.

3.3 Generate Domain Classes from WSDL

To consume web service, we need to generate Java classes from WSDL. We can use JAXB to perform this task. Find the code snippet from pom.xml.
<plugin>
 <groupId>org.jvnet.jaxb2.maven2</groupId>
 <artifactId>maven-jaxb2-plugin</artifactId>
 <version>0.14.0</version>
 <executions>
   <execution>
	<goals>
           <goal>generate</goal>
	</goals>
   </execution>
 </executions>
 <configuration>
   <schemaLanguage>WSDL</schemaLanguage>
   <generatePackage>com.concretepage.wsdl</generatePackage>
   <schemas>
	<schema>
	   <url>http://localhost:8080/soapws/articles.wsdl</url>
	</schema>
   </schemas>
 </configuration>
</plugin> 
When we run command mvn eclipse:eclipse or mvn clean package then JAXB will generate Java classes from the configured WSDL URL i.e. /soapws/articles.wsdl. The Java classes will be generated in com.concretepage.wsdl package under the target/generated-sources/xjc directory. The generated Java classes in web service client are same as generated in web service producer.

AddArticleRequest.java
AddArticleResponse.java
ArticleInfo.java
DeleteArticleRequest.java
DeleteArticleResponse.java
GetAllArticlesRequest.java
GetAllArticlesResponse.java
GetArticleByIdRequest.java
GetArticleByIdResponse.java
ObjectFactory.java
package-info.java
ServiceStatus.java
UpdateArticleRequest.java
UpdateArticleResponse.java

3.4 Create Service for CRUD Operation

We will create a service client to perform CRUD operation. Service class will extend WebServiceGatewaySupport which is the super class for application classes that need web service access. WebServiceGatewaySupport offers Marshaller and Unmarshaller and default URI properties. The Gateway provides a method getWebServiceTemplate() that returns instance of WebServiceTemplate. Using this template we send request and receive response from web service producer. Generated Java classes are used as request and response object.
ArticleClient.java
package com.concretepage;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.soap.client.core.SoapActionCallback;
import com.concretepage.wsdl.AddArticleRequest;
import com.concretepage.wsdl.AddArticleResponse;
import com.concretepage.wsdl.ArticleInfo;
import com.concretepage.wsdl.DeleteArticleRequest;
import com.concretepage.wsdl.DeleteArticleResponse;
import com.concretepage.wsdl.GetAllArticlesRequest;
import com.concretepage.wsdl.GetAllArticlesResponse;
import com.concretepage.wsdl.GetArticleByIdRequest;
import com.concretepage.wsdl.GetArticleByIdResponse;
import com.concretepage.wsdl.UpdateArticleRequest;
import com.concretepage.wsdl.UpdateArticleResponse;

public class ArticleClient extends WebServiceGatewaySupport  {
	public GetArticleByIdResponse getArticleById(long articleId) {
		GetArticleByIdRequest request = new GetArticleByIdRequest();
		request.setArticleId(articleId);
		GetArticleByIdResponse response = (GetArticleByIdResponse) getWebServiceTemplate().marshalSendAndReceive(
				request, new SoapActionCallback("http://localhost:8080/soapws/getArticleByIdRequest"));
		return response;
	}
	public GetAllArticlesResponse getAllArticles() {
		GetAllArticlesRequest request = new GetAllArticlesRequest();
		GetAllArticlesResponse response = (GetAllArticlesResponse) getWebServiceTemplate().marshalSendAndReceive(
				request, new SoapActionCallback("http://localhost:8080/soapws/getAllArticlesRequest"));
     	        return response;
	}	
	public AddArticleResponse addArticle(String title, String category) {
		AddArticleRequest request = new AddArticleRequest();
		request.setTitle(title);
		request.setCategory(category);
		AddArticleResponse response = (AddArticleResponse) getWebServiceTemplate().marshalSendAndReceive(
				request, new SoapActionCallback("http://localhost:8080/soapws/addArticleRequest"));
     	        return response;
	}	
	public UpdateArticleResponse updateArticle(ArticleInfo articleInfo) {
		UpdateArticleRequest request = new UpdateArticleRequest();
		request.setArticleInfo(articleInfo);
		UpdateArticleResponse response = (UpdateArticleResponse) getWebServiceTemplate().marshalSendAndReceive(
				request, new SoapActionCallback("http://localhost:8080/soapws/updateArticleRequest"));
     	        return response;
	}	
	public DeleteArticleResponse deleteArticle(long articleId) {
		DeleteArticleRequest request = new DeleteArticleRequest();
		request.setArticleId(articleId);
		DeleteArticleResponse response = (DeleteArticleResponse) getWebServiceTemplate().marshalSendAndReceive(
				request, new SoapActionCallback("http://localhost:8080/soapws/deleteArticleRequest"));
     	        return response;
	}		
} 

3.5 Configure Web Service Components

To serialize and deserialize XML requests, Spring uses Jaxb2Marshaller. We need to set Marshaller and Unmarshaller in our service client. Find the Java configuration class.
WSConfigClient.java
package com.concretepage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;

@Configuration
public class WSConfigClient {
	@Bean
	public Jaxb2Marshaller marshaller() {
		Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
		marshaller.setContextPath("com.concretepage.wsdl");
		return marshaller;
	}
	@Bean
	public ArticleClient articleClient(Jaxb2Marshaller marshaller) {
		ArticleClient client = new ArticleClient();
		client.setDefaultUri("http://localhost:8080/soapws/articles.wsdl");
		client.setMarshaller(marshaller);
		client.setUnmarshaller(marshaller);
		return client;
	}
} 

3.6 Test SOAP Web Service Client Application

Find the class annotated with @SpringBootApplication.
MySpringApplicationClient.java
package com.concretepage;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.concretepage.wsdl.AddArticleResponse;
import com.concretepage.wsdl.ArticleInfo;
import com.concretepage.wsdl.DeleteArticleResponse;
import com.concretepage.wsdl.GetAllArticlesResponse;
import com.concretepage.wsdl.GetArticleByIdResponse;
import com.concretepage.wsdl.ServiceStatus;
import com.concretepage.wsdl.UpdateArticleResponse;

@SpringBootApplication
public class MySpringApplicationClient {  
	public static void main(String[] args) {
		SpringApplication.run(MySpringApplicationClient.class, args);
        }       
	@Bean
	CommandLineRunner lookup(ArticleClient articleClient) {
		return args -> {
			System.out.println("--- Get Article by Id ---");
			GetArticleByIdResponse articleByIdResponse = articleClient.getArticleById(1);
			ArticleInfo articleInfo = articleByIdResponse.getArticleInfo();
			System.out.println(articleInfo.getArticleId() + ", "+ articleInfo.getTitle()
			     + ", " + articleInfo.getCategory());
			
			System.out.println("--- Get all Articles ---");
			GetAllArticlesResponse allArticlesResponse = articleClient.getAllArticles();
			allArticlesResponse.getArticleInfo().stream()
			   .forEach(e -> System.out.println(e.getArticleId() + ", "+ e.getTitle() + ", " + e.getCategory()));
			
			System.out.println("--- Add Article ---");
		        String title = "Spring REST Security using Hibernate";
		        String category = "Spring";
			AddArticleResponse addArticleResponse = articleClient.addArticle(title, category);
			articleInfo = addArticleResponse.getArticleInfo();
			if (articleInfo != null) {
			  System.out.println(articleInfo.getArticleId() + ", "+ articleInfo.getTitle()
			       + ", " + articleInfo.getCategory());
			}
			ServiceStatus serviceStatus = addArticleResponse.getServiceStatus();
			System.out.println("StatusCode: " + serviceStatus.getStatusCode() + 
					", Message: " + serviceStatus.getMessage());
			
			System.out.println("--- Update Article ---");
			articleInfo = new ArticleInfo();
			articleInfo.setArticleId(1);
			articleInfo.setTitle("Update:Java Concurrency");
			articleInfo.setCategory("Java");
			UpdateArticleResponse updateArticleResponse = articleClient.updateArticle(articleInfo);
			serviceStatus = updateArticleResponse.getServiceStatus();
			System.out.println("StatusCode: " + serviceStatus.getStatusCode() + 
					", Message: " + serviceStatus.getMessage());
			System.out.println("--- Delete Article ---");
			long articleId = 3;
			DeleteArticleResponse deleteArticleResponse = articleClient.deleteArticle(articleId);
			serviceStatus = deleteArticleResponse.getServiceStatus();
			System.out.println("StatusCode: " + serviceStatus.getStatusCode() + 
					", Message: " + serviceStatus.getMessage());			
		};
	}	
} 
To run the SOAP web service consumer first make sure that SOAP web service producer is running and following URL is accessible.
http://localhost:8080/soapws/articles.wsdl 
Now run the SOAP web service client as following.
1. Using Maven Command: Download the project source code using the download link given at the end of article. Go to the root folder of the project using command prompt and run the command.
mvn spring-boot:run 
2. Using Eclipse: Download the project source code. Import the project into eclipse. Using command prompt, go to the root folder of the project and run.
mvn clean eclipse:eclipse 
and then refresh the project in eclipse. Run Main class MySpringApplicationClient by clicking Run as -> Java Application.

3. Using Executable JAR: Using command prompt, go to the root folder of the project and run the command.
mvn clean package 
We will get executable JAR soap-ws-client-0.0.1-SNAPSHOT.jar in target folder. Run this JAR as
java -jar target/soap-ws-client-0.0.1-SNAPSHOT.jar 

Find the output of SOAP web service client.
--- Get Article by Id ---
1, Java Concurrency, Java
--- Get all Articles ---
1, Java Concurrency, Java
2, Spring Boot Getting Started, Spring Boot
--- Add Article ---
3, Spring REST Security using Hibernate, Spring
StatusCode: SUCCESS, Message: Content Added Successfully
--- Update Article ---
StatusCode: SUCCESS, Message: Content Updated Successfully
--- Delete Article ---
StatusCode: SUCCESS, Message: Content Deleted Successfully 

4. References

Producing a SOAP web service
Consuming a SOAP web service

5. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us