Spring Boot SOAP Web Service
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.
Contents
- 1. Technologies Used
- 2. Producing SOAP Web Service for CRUD
- 2.1 Project Structure in Eclipse
- 2.2 Create Maven File
- 2.3 Create XML Schema for CRUD Operation
- 2.4 Generate Domain Classes from XML Schema
- 2.5 Configure Web Service Bean
- 2.6 Create Web Service Endpoint for CRUD Operation
- 2.7 Create Database Table
- 2.8 Spring Boot application.properties
- 2.9 Create Service for CRUD using JPA CrudRepository
- 2.10 Run SOAP Web Service
- 2.11 Test SOAP Web Service using Soap UI
- 3. Spring SOAP Web Service Client
- 4. References
- 5. Download Source Code
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.2.2 Create Maven File
Find thepom.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>
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>
2.4 Generate Domain Classes from XML Schema
We will generate Java classes from XSD file. In our example we havearticles.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>
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; } }
@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 inapplication.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 usingCrudRepository
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); }
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); }
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); } }
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); } }
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
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
java -jar target/soap-ws-0.0.1-SNAPSHOT.jar
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
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>
<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>
<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>
<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>
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>
<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>
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>
<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>
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>
<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.3.2 Create Maven File
Find thepom.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>
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 frompom.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>
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 extendWebServiceGatewaySupport
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 usesJaxb2Marshaller
. 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()); }; } }
http://localhost:8080/soapws/articles.wsdl
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
mvn clean eclipse:eclipse
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
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 serviceConsuming a SOAP web service