Writing and Consuming SOAP Webservice with Spring

In the era of RESTful Web Services, I got a chance to consume SOAP Web Service. To do the same I chosen Spring, reason being we are already using Spring as backend framework in our project and secondly it provides an intuitive way to interact service(s) with well-defined boundaries to promote reusability and portability through WebServiceTemplate.

Assuming you already know about SOAP Web Services, let’s start creating hello-world soap service running on port 9999 and client to consume the same, following below steps:

Step 1: Go to start.spring.io and create a new project soap-server adding the Web starters, based on the following image:

soap-server

Step 2: Edit SoapServerApplication.java to publish the hello-world service at Endpoint – http://localhost:9999/service/hello-world, as follows:


package com.arpit.soap.server.main;

import javax.xml.ws.Endpoint;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.arpit.soap.server.service.impl.HelloWorldServiceImpl;

@SpringBootApplication
public class SoapServerApplication implements CommandLineRunner {

	@Value("${service.port}")
	private String servicePort;

	@Override
	public void run(String... args) throws Exception {
		Endpoint.publish("http://localhost:" + servicePort
				+ "/service/hello-world", new HelloWorldServiceImpl());
	}

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


Step 3: Edit application.properties to specify the application name, port and port number of hello-world service, as follows:

server.port=9000
spring.application.name=soap-server

## Soap Service Port
service.port=9999

Step 4: Create additional packages as com.arpit.soap.server.service and com.arpit.soap.server.service.impl to define the Web Service and it’s implementation, as follows:

HelloWorldService.java

package com.arpit.soap.server.service;

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

import com.arpit.soap.server.model.ApplicationCredentials;

@WebService
public interface HelloWorldService {

	@WebMethod(operationName = "helloWorld", action = "https://aggarwalarpit.wordpress.com/hello-world/helloWorld")
	String helloWorld(final String name,
			@WebParam(header = true) final ApplicationCredentials credential);

}

@WebService specified above marks a Java class as implementing a Web Service, or a Java interface as defining a Web Service interface.

@WebMethod specified above marks a Java method as a Web Service operation.

@WebParam specified above customize the mapping of an individual parameter to a Web Service message part and XML element.

HelloWorldServiceImpl.java

package com.arpit.soap.server.service.impl;

import javax.jws.WebService;

import com.arpit.soap.server.model.ApplicationCredentials;
import com.arpit.soap.server.service.HelloWorldService;

@WebService(endpointInterface = "com.arpit.soap.server.service.HelloWorldService")
public class HelloWorldServiceImpl implements HelloWorldService {

	@Override
	public String helloWorld(final String name,
			final ApplicationCredentials credential) {
		return "Hello World from " + name;
	}
}

Step 5: Move to soap-server directory and run command: mvn spring-boot:run. Once running, open http://localhost:9999/service/hello-world?wsdl to view the WSDL for the hello-world service.

Next, we will create soap-client which will consume our newly created hello-world service.

Step 6: Go to start.spring.io and create a new project soap-client adding the Web, Web Services starters, based on the following image:

soap-client.png

Step 7: Edit SoapClientApplication.java to create a request to hello-world web service, sending the same to soap-server along with header and get the response from it, as follows:


package com.arpit.soap.client.main;

import java.io.IOException;
import java.io.StringWriter;

import javax.xml.bind.JAXBElement;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.xml.transform.StringSource;

import com.arpit.soap.server.service.ApplicationCredentials;
import com.arpit.soap.server.service.HelloWorld;
import com.arpit.soap.server.service.HelloWorldResponse;
import com.arpit.soap.server.service.ObjectFactory;

@SpringBootApplication
@ComponentScan("com.arpit.soap.client.config")
public class SoapClientApplication implements CommandLineRunner {

	@Autowired
	@Qualifier("webServiceTemplate")
	private WebServiceTemplate webServiceTemplate;

	@Value("#{'${service.soap.action}'}")
	private String serviceSoapAction;

	@Value("#{'${service.user.id}'}")
	private String serviceUserId;

	@Value("#{'${service.user.password}'}")
	private String serviceUserPassword;

	public static void main(String[] args) {
		SpringApplication.run(SoapClientApplication.class, args);
		System.exit(0);
	}

	public void run(String... args) throws Exception {
		final HelloWorld helloWorld = createHelloWorldRequest();
		@SuppressWarnings("unchecked")
		final JAXBElement<HelloWorldResponse> jaxbElement = (JAXBElement<HelloWorldResponse>) sendAndRecieve(helloWorld);
		final HelloWorldResponse helloWorldResponse = jaxbElement.getValue();
		System.out.println(helloWorldResponse.getReturn());
	}

	private Object sendAndRecieve(HelloWorld seatMapRequestType) {
		return webServiceTemplate.marshalSendAndReceive(seatMapRequestType,
				new WebServiceMessageCallback() {
					public void doWithMessage(WebServiceMessage message)
							throws IOException, TransformerException {
						SoapMessage soapMessage = (SoapMessage) message;
						soapMessage.setSoapAction(serviceSoapAction);
						org.springframework.ws.soap.SoapHeader soapheader = soapMessage
								.getSoapHeader();
						final StringWriter out = new StringWriter();
						webServiceTemplate.getMarshaller().marshal(
								getHeader(serviceUserId, serviceUserPassword),
								new StreamResult(out));
						Transformer transformer = TransformerFactory
								.newInstance().newTransformer();
						transformer.transform(new StringSource(out.toString()),
								soapheader.getResult());
					}
				});
	}

	private Object getHeader(final String userId, final String password) {
		final https.aggarwalarpit_wordpress.ObjectFactory headerObjectFactory = new https.aggarwalarpit_wordpress.ObjectFactory();
		final ApplicationCredentials applicationCredentials = new ApplicationCredentials();
		applicationCredentials.setUserId(userId);
		applicationCredentials.setPassword(password);
		final JAXBElement<ApplicationCredentials> header = headerObjectFactory
				.createApplicationCredentials(applicationCredentials);
		return header;
	}

	private HelloWorld createHelloWorldRequest() {
		final ObjectFactory objectFactory = new ObjectFactory();
		final HelloWorld helloWorld = objectFactory.createHelloWorld();
		helloWorld.setArg0("Arpit");
		return helloWorld;
	}

}

Step 8: Next, create additional package com.arpit.soap.client.config to configure WebServiceTemplate, as follows:

ApplicationConfig.java

package com.arpit.soap.client.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.transport.http.HttpComponentsMessageSender;

@Configuration
@EnableWebMvc
public class ApplicationConfig extends WebMvcConfigurerAdapter {

	@Value("#{'${service.endpoint}'}")
	private String serviceEndpoint;

	@Value("#{'${marshaller.packages.to.scan}'}")
	private String marshallerPackagesToScan;

	@Value("#{'${unmarshaller.packages.to.scan}'}")
	private String unmarshallerPackagesToScan;

	@Bean
	public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
		return new PropertySourcesPlaceholderConfigurer();
	}

	@Bean
	public SaajSoapMessageFactory messageFactory() {
		SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
		messageFactory.afterPropertiesSet();
		return messageFactory;
	}

	@Bean
	public Jaxb2Marshaller marshaller() {
		Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
		marshaller.setPackagesToScan(marshallerPackagesToScan.split(","));
		return marshaller;
	}

	@Bean
	public Jaxb2Marshaller unmarshaller() {
		Jaxb2Marshaller unmarshaller = new Jaxb2Marshaller();
		unmarshaller.setPackagesToScan(unmarshallerPackagesToScan.split(","));
		return unmarshaller;
	}

	@Bean
	public WebServiceTemplate webServiceTemplate() {
		WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
				messageFactory());
		webServiceTemplate.setMarshaller(marshaller());
		webServiceTemplate.setUnmarshaller(unmarshaller());
		webServiceTemplate.setMessageSender(messageSender());
		webServiceTemplate.setDefaultUri(serviceEndpoint);
		return webServiceTemplate;
	}

	@Bean
	public HttpComponentsMessageSender messageSender() {
		HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
		return httpComponentsMessageSender;
	}
}

Step 9: Edit application.properties to specify the application name, port and hello-world soap web service configurations, as follows:

server.port=9000
spring.application.name=soap-client

## Soap Service Configuration

service.endpoint=http://localhost:9999/service/hello-world
service.soap.action=https://aggarwalarpit.wordpress.com/hello-world/helloWorld
service.user.id=arpit
service.user.password=arpit
marshaller.packages.to.scan=com.arpit.soap.server.service
unmarshaller.packages.to.scan=com.arpit.soap.server.service

service.endpoint specified above is the URL provided to the service user to invoke the services exposed by the service provider.

service.soap.action specifies which process or program that need to be called when a request is sent by the service requester and also defines the relative path of the process/program.

marshaller.packages.to.scan specifies the packages to scan at time of marshalling before sending the request to the server.

unmarshaller.packages.to.scan specifies the packages to scan at time of unmarshalling after receiving the request from the server.

Now, we will generate Java Objects from WSDL using wsimport and copy it to the soap-client project executing below command on the terminal:

wsimport -keep -verbose http://localhost:9999/service/hello-world?wsdl

Step 10: Move to soap-client directory and run command: mvn spring-boot:run. Once command finishes we will see “Hello World from Arpit” as response from hello-world soap service on console.

While running if you are getting error as – Unable to marshal type “com.arpit.soap.server.service.HelloWorld” as an element because it is missing an @XmlRootElement annotation┬áthen add @XmlRootElement(name = “helloWorld”, namespace = “http://service.server.soap.arpit.com/ “) to the com.arpit.soap.server.service.HelloWorld, where namespace should be matched from xmlns:ser defined in soap envelope, as below:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.server.soap.arpit.com/">
   <soapenv:Header>
      <ser:arg1>
         <userId>arpit</userId>
         <password>arpit</password>
      </ser:arg1>
   </soapenv:Header>
   <soapenv:Body>
      <ser:helloWorld>
         <!--Optional:-->
         <arg0>Arpit</arg0>
      </ser:helloWorld>
   </soapenv:Body>
</soapenv:Envelope>

The complete source code is hosted on github.