This page will introduce the Spring Boot SOAP network service instance. Here, we will create SOAP web service producers and consumers for CRUD operations. For producers, we need to create an XML schema to create WSDL. For WSDL, we use JavaConfig to configure the DefaultWsdl11Definition. The producer configuration class is annotated as @ EnableWs and extends the WsConfigurerAdapter. SOAP network service endpoints are created using Spring annotations, such as @ Endpoint, @ PayloadRoot, and @ ResponsePayload. To handle database operations, we use JPA CrudRepository.
In the SOAP web service client application, we need to use the WSDL generated by the SOAP web service producer to generate Java source code. We need to create a service class that extends WebServiceGatewaySupport and provide WebServiceTemplate to send requests and receive responses. In order to serialize and deserialize XML requests, we need to configure Jaxb2Marshaller.
Now let's take a look at the complete example of step-by-step implementation of SOAP network service producers and consumers using Spring Boot.
1. Demo tool version
- Java 16
- Spring 5.3.10
- Spring Boot 2.5.5
- MojoHaus JAXB2 Maven Plugin 2.5.0
- WSDL4J 1.6.3
- JVNET JAXB2 Maven Plugin 0.14.0
- Maven 3.8.1
- MySQL 5.5
2. Make SOAP network service for CRUD
We will create a Web service producer application. We will CRUD the article. We need to create an XML Schema in which we will define XML requests and XML responses for create, read, update, and delete operations. The application will generate WSDL based on the defined XML schema. We will use the Web service client application and SOAP user interface to test our Web service producer. Now look at the complete Web service producer example, step by step.
2.1 project structure
2.2 creating Maven files
Find pom.xml to make the 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 launcher for spring web services.
wsdl4j: it allows the creation, presentation, and manipulation of WSDL documents.
Jaxb2 Maven plugin: it generates Java classes from XML schemas.
2.3 creating an XML schema for CRUD operations
We will create an XML Schema (XSD) to define the network service domain. Spring Web services will automatically export xsds as WSDL. In our example, we are creating an 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 schemas for requests and responses to create, read, update, and delete articles.
2.4 generate Domain class from XML schema
We will generate Java classes from xsd files. In our example, we have an article. xsd file for CRUD operations. In pom.xml, we have configured jaxb2 Maven plugin, which is used to generate Java classes from XML schema. Jaxb2 Maven plugin is configured in pom.xml as follows.
<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 the command mvn eclipse:eclipse or mvn clean package, xjc goal will run. It will select the XSD file from the src/main/resources/xsds directory and generate com.concretepage.gs in the target / generated sources / JAXB directory_ Domain class of WS package. Find the Java classes for all generated 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 configuring a network service Bean
We will create a Java configuration class for Spring web services, annotate it with @ EnableWs, and extend WsConfigurerAdapter. Now we will configure the Web service DefaultWsdl11Definition Bean, as shown below.
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 used to configure WSDL definitions, such as port type name, location URI, target namespace, schema, etc.
XSD schema represents an abstraction of XSD schema.
ServletRegistrationBean configures application context, URL mapping, etc.
@EnableWs is used with @ Configuration to define Spring web services in WsConfigurerAdapter.
2.6 creating a network service endpoint for CRUD operations
We will create a Web service Endpoint for CRUD operations. The Endpoint class accepts the network service request and returns the network service response. Here, we need to use Java classes generated from XSD files to process requests and responses.
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; } }
Locate the Spring Web service annotation used to create the Endpoint.
@Endpoint: the class annotated with @ endpoint becomes the web service endpoint.
@PayloadRoot: the method annotated with @ PayloadRoot becomes an Endpoint method that accepts incoming requests and returns responses. It has the properties localPart and namespace. The localPart attribute is required and marks the local part of the payload root element. The attribute namespace is optional and marks the namespace of the payload root element.
@ResponsePayload: annotation @ ResponsePayload limits the response of the method to the payload of the response.
2.7 creating database tables
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 application.properties
In the Spring Boot application, we need to configure the data source, JPA properties and logs in the application.properties file located in the classpath. These properties will be automatically 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 services for CRUD using JPA CrudRepository
We will use the CrudRepository implementation to create a service class for CRUD operations. CrudRepository provides common CRUD operations for specific types of repositories. CrudRepository is a data interface of spring. In order to use it, we need to create our interface by extending CrudRepository. Spring automatically provides CrudRepository implementation classes 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 name with find...By, read...By, query...By, count...By and get...By. Before by, we can add an expression, such as Distinct. After by, we need to add the attribute name of our entity. We can also use the @ 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 classes of our repository interfaces will be automatically created by Spring. Now we will create the service and use the 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 running SOAP network service
Locate the class with the @ SpringBootApplication annotation.
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 a table in MySQL, as shown in the example. Then, we can run the SOAP web service producer in the following ways.
1. Use Maven command: use the download link given at the end of the text to download the project source code. Use the command prompt to enter the root folder of the project and run the command.
mvn spring-boot:run
The Tomcat server will be started.
2. Using Eclipse: download the source code of the project. Import the project into eclipse. Using the command prompt, go to the root directory of the project and run.
mvn clean eclipse:eclipse
Then refresh the project in eclipse. Run the main class MySpringApplication by clicking run as - > java application. The Tomcat server will be started.
3. Use executable jars: use the command prompt to enter the root folder of the project and run the command.
mvn clean package
We will get the executable JAR soap-ws-0.0.1-SNAPSHOT.jar in the target folder. Run the JAR as follows
java -jar target/soap-ws-0.0.1-SNAPSHOT.jar
The Tomcat server will be started.
Now we are ready to test our SOAP web service producer application. The following URL will run successfully in the browser and return the WSDL.
http://localhost:8080/soapws/articles.wsdl
2.11 testing SOAP web services using SOAP UI
We will test our SOAP web service producers using the SOAP user interface. We will use the following WSDL URL to create the SOAP request.
http://localhost:8080/soapws/articles.wsdl
Now find the SOAP request and response for the CRUD operation.
1. READ
(a). Request: read the article according to the 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>
As shown in the figure
(b). Request: read articles used
<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 the WSDL provided by the spring web service producer. Spring web services use the Spring OXM module to serialize and deserialize XML requests. We will create a service client to CRUD the article. We will use Spring Boot to run our SOAP web Services client application.
3.1 project structure
3.2 creating Maven files
Locate pom.xml for the 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 solves the dependency of spring web service core.
Maven jaxb2 plugin: it can generate Java classes from WSDL.
3.3 generate Domain class from WSDL
In order to use web services, 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 the command mvn eclipse:eclipse or mvn clean package, JAXB will generate Java classes from the configured WSDL URL, / soapws/articles.wsdl. These Java classes will be generated in the target / generated sources / xjc directory of the com.concretepage.wsdl package. The Java classes generated in the Web service client are the same as those generated in the 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 operations. The Service class will extend WebServiceGatewaySupport, which is an enhanced class of application classes that require Web Service access. WebServiceGatewaySupport provides Marshall and Unmarshaller and default URI properties. The gateway provides a method of getWebServiceTemplate() to return an instance of WebServiceTemplate. Using this template, we send a request and receive a response from the Web Service producer. The generated Java classes are used as request and response objects.
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 configuring network service components
To serialize and deserialize XML requests, Spring uses jaxb2 Marshall. We need to set Marshall and Unmarshaller on our service client. Locate 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 testing SOAP web service client applications
Locate the class with the @ SpringBootApplication annotation.
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 a SOAP web service consumer, first ensure that the SOAP web service producer is running and can access the following URL.
http://localhost:8080/soapws/articles.wsdl
Now run the SOAP web service client as follows.
1. Use Maven command: use the download link given at the end of the text to download the project source code. Use the command prompt to enter the root folder of the project and run the command.
mvn spring-boot:run
2. Using Eclipse: download the source code of the project. Import the project into eclipse. Using the command prompt, go to the root directory of the project and run.
mvn clean eclipse:eclipse
Then refresh the project in eclipse. Run the main class MySpringApplicationClient by clicking run as - > java application.
3. Use executable jars: use the command prompt to enter the root folder of the project and run the command.
mvn clean package
We will get the executable JAR soap-ws-client-0.0.1-SNAPSHOT.jar in the target folder. Run the JAR as follows
java -jar target/soap-ws-client-0.0.1-SNAPSHOT.jar
Find the output of the 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
reference
[1]Producing a SOAP web service
[2]Consuming a SOAP web service
Source download
Extraction code: mao4