Introduction
This tutorial will show you how we can create and publish SOAP based webservice in Contract-first approach using Apache cxf, Spring and Maven. There are mainly two approaches to create the Webservice – Contract-first & Contract-last. The Contract-first approach tells us to create first WSDL and then create end-point interface and implementation class. The Contract-last approach tells us to create first end-point interface and implementation class then create WSDL file.
For this tutorial we will create one maven web project in Eclipse.
If you already have an idea on how to create a maven project in Eclipse will be great otherwise I will tell you here how to create a maven project in Eclipse.
Prerequisites
The following configurations are required in order to run the application
Eclipse Neon, JDK 1.8, Tomcat 8, Maven 3.6.0
Apache CXF 3.3.2 and Spring 5.1.8
Example Implementation
Now we will see the below steps how to implement contract first SOAP web service.
Creating Project
Create a maven based web project in Eclipse.
Go to File -> New -> Other. On popup window under Maven select Maven Project. Then click on Next. Select the workspace location – either default or browse the location. Click on Next. Now in next window select the row as highlighted from the below list of archtypes and click on Next button.
maven-arctype-webapp
Now enter the required fields (Group Id, Artifact Id) as shown below:
Group Id : com.roytuts
Artifact Id : soap-contract-first-webservice
Updating Build File
Modify the pom.xml file as shown below to include required dependencies and plugins.
<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.roytuts</groupId>
<artifactId>soap-contract-first-webservice</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version>
<cxf.version>3.3.2</cxf.version>
<spring.version>5.1.8.RELEASE</spring.version>
</properties>
<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Apache cxf dependencies -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- servlet & jsp -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
<finalName>soap-contract-first-webservice</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<configuration>
<excludes>
<exclude>/xsd/*</exclude>
</excludes>
</configuration>
</configuration>
</plugin>
<!-- Generate Java classes from WSDL during build -->
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<!-- which source folder the generated classes should be placed in
a package -->
<sourceRoot>${project.basedir}/src/generated-sources/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<!-- put the wsdl file in this location -->
<wsdl>${project.basedir}/src/main/resources/wsdl/user.wsdl</wsdl>
<extraargs>
<extraarg>-p</extraarg>
<extraarg>com.roytuts.soap.contract.first</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Configuring Build Path
If you see JRE System Library[J2SE-1.5] then change the version by below process
Do right-click on the project and go to Build -> Configure build path, under Libraries tab click on JRE System Library[J2SE-1.5], click on Edit button and select the appropriate jdk 1.8 from the next window. Click on Finish then Ok.
Creating XSD
Create an XSD (schema definition file) called user.xsd under src/main/resources/xsd folder.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="https://roytuts.com/UserService"
targetNamespace="https://roytuts.com/UserService"
elementFormDefault="qualified">
<xs:element name="getUserDetailsRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getUserDetailsResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="user" type="tns:user" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="user">
<xs:sequence>
<xs:element name="id" type="xs:int" />
<xs:element name="name" type="xs:string" />
<xs:element name="email" type="xs:string" />
<xs:element name="address" type="tns:address" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="address">
<xs:sequence>
<xs:element name="street" type="xs:string" />
<xs:element name="city" type="xs:string" />
<xs:element name="state" type="xs:string" />
<xs:element name="zip" type="xs:int" />
<xs:element name="country" type="xs:string" />
<xs:element name="addressType" type="tns:addressType" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="addressType">
<xs:restriction base="xs:string">
<xs:enumeration value="PERMANENT" />
<xs:enumeration value="COMMUNICATION" />
<xs:enumeration value="OFFICIAL" />
</xs:restriction>
</xs:simpleType>
</xs:schema>
In the above XSD we have basically two main elements – getUserDetailsRequest
and getUserDetailsResponse
.
So getUserDetailsRequest
will act as an input and getUserDetailsResponse
will act as an output based on input.
Creating WSDL
Now create a WSDL (web service definition language) file user.wsdl under src/main/resources folder.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="https://roytuts.com/UserService"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="https://roytuts.com/UserService">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import
namespace="https://roytuts.com/UserService"
schemaLocation="../xsd/user.xsd" />
</xsd:schema>
</wsdl:types>
<wsdl:message name="getUserDetailsRequest">
<wsdl:part element="tns:getUserDetailsRequest"
name="getUserDetailsRequest" />
</wsdl:message>
<wsdl:message name="getUserDetailsResponse">
<wsdl:part element="tns:getUserDetailsResponse"
name="getUserDetailsResponse" />
</wsdl:message>
<wsdl:portType name="UserPort">
<wsdl:operation name="getUserDetailsRequest">
<wsdl:input message="tns:getUserDetailsRequest"
name="userDetailsInput" />
<wsdl:output message="tns:getUserDetailsResponse"
name="userDetailsOutput" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="UserBinding" type="tns:UserPort">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="getUserDetailsRequest">
<soap:operation soapAction="" />
<wsdl:input name="userDetailsInput">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="userDetailsOutput">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="UserService">
<wsdl:port binding="tns:UserBinding" name="UserService">
<soap:address location="/UserService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
In the definition file <wsdl:types/> defines the types of input and output requests, so in this file we have included the external XSD file user.xsd otherwise we had to define the types inside <wsdl:types/>.
In <wsdl:message name=”…”> the name is different from what is declared as element’s name in user.xsd file.
In <wsdl:part element=”…” name=”…”> the name must be same as what is declared as element’s name in user.xsd file.
Then we have <wsdl:portType/>, here the operation name could be as per your choice. The name in <wsdl:operation/> could be anything. The message in <wsdl:input/> or <wsdl:output/> must match with <wsdl:message/> name but the name in <wsdl:input/> or <wsdl:output/> could be anything.
The name in <wsdl:binding/> could be anything but type must match with the name in <wsdl:portType/>. Then inside <wsdl:operation/> we have <wsdl:input/> & <wsdl:output/> and their names must match with the names of <wsdl:input/> and <wsdl:output/> inside <wsdl:portType/>.
The name could be anything for <wsdl:service/>. The binding in <wsdl:port/> must be same as name of <wsdl:binding/>.
Lastly <soap:address/> where host & port have been removed because it will be running in the same server where deployed.
Building Project
Now build the maven project, you will see there are few classes generated under the package com.roytuts.soap.contract.first.
Creating Some Data
We are not using any database here to fetch data so we will create one mock data class which will give us data based on matching input.
package com.roytuts.soap.contract.first.data;
import java.util.ArrayList;
import java.util.List;
import com.roytuts.soap.contract.first.Address;
import com.roytuts.soap.contract.first.AddressType;
import com.roytuts.soap.contract.first.User;
public class UserMockData {
private List<User> users = new ArrayList<User>();
public UserMockData() {
User u1 = new User();
u1.setId(1);
u1.setName("Sumit");
u1.setEmail("sumit.ghosh@email.com");
Address a1 = new Address();
a1.setStreet("Garfa");
a1.setCity("Kolkata");
a1.setState("WB");
a1.setCountry("India");
a1.setZip(700030);
a1.setAddressType(AddressType.COMMUNICATION);
u1.setAddress(a1);
User u2 = new User();
u2.setId(2);
u2.setName("Loku");
u2.setEmail("debabrata.poddar@email.com");
Address a2 = new Address();
a2.setStreet("Birati");
a2.setCity("Kolkata");
a2.setState("WB");
a2.setCountry("India");
a2.setZip(700130);
a2.setAddressType(AddressType.COMMUNICATION);
u2.setAddress(a2);
User u3 = new User();
u3.setId(3);
u3.setName("Souvik");
u3.setEmail("souvik.sanyal@email.com");
Address a3 = new Address();
a3.setStreet("Kalighat");
a3.setCity("Kolkata");
a3.setState("WB");
a3.setCountry("India");
a3.setZip(700150);
a3.setAddressType(AddressType.COMMUNICATION);
u3.setAddress(a3);
User u4 = new User();
u4.setId(4);
u4.setName("Liton");
u4.setEmail("liton.sarkar@email.com");
Address a4 = new Address();
a4.setStreet("Sukanta Nagar");
a4.setCity("Kolkata");
a4.setState("WB");
a4.setCountry("India");
a4.setZip(700098);
a4.setAddressType(AddressType.COMMUNICATION);
u4.setAddress(a4);
User u5 = new User();
u5.setId(5);
u5.setName("Debina");
u5.setEmail("debina.guha@email.com");
Address a5 = new Address();
a5.setStreet("Kestopur");
a5.setCity("Kolkata");
a5.setState("WB");
a5.setCountry("India");
a5.setZip(700091);
a5.setAddressType(AddressType.COMMUNICATION);
u5.setAddress(a5);
users.add(u1);
users.add(u2);
users.add(u3);
users.add(u4);
users.add(u5);
}
public User getUser(int id) {
for (User user : users) {
if (id == user.getId()) {
return user;
}
}
return null;
}
}
SOAP Service Implementation Class
Maven already generated end-point interface for us, so we will create an implementation class.
package com.roytuts.soap.contract.first.service.impl;
import javax.jws.WebService;
import com.roytuts.soap.contract.first.User;
import com.roytuts.soap.contract.first.UserPort;
import com.roytuts.soap.contract.first.data.UserMockData;
@WebService(endpointInterface = "com.roytuts.soap.contract.first.UserPort")
public class UserPortImpl implements UserPort {
private UserMockData mockData;
public UserPortImpl(UserMockData mockData) {
this.mockData = mockData;
}
@Override
public User getUserDetailsRequest(int id) {
return mockData.getUser(id);
}
}
Configuring Address Endpoint
Create cxf-config.xml file inside src/main/resources folder for configuring beans.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<bean id="mockData"
class="com.roytuts.soap.contract.first.data.UserMockData" />
<bean id="userPort"
class="com.roytuts.soap.contract.first.service.impl.UserPortImpl">
<constructor-arg ref="mockData" />
</bean>
<jaxws:endpoint id="UserService" implementor="#userPort"
address="/user" />
</beans>
In the above spring configuration we have defined the endpoint for webservice.
Updating Deployment Descriptor
Now update the web.xml file as below to define our servlet configurations.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>SOAP Web Service Contract First</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:cxf-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<description>CXF Servlet</description>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
Deploying into Tomcat
Now deploy the project into Tomcat 8 server, you will see output similar to the below:
Initializing Spring root WebApplicationContext
7:49:54 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
7:49:57 PM org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://impl.service.first.contract.soap.roytuts.com/}UserPortImplService from class com.roytuts.soap.contract.first.UserPort
7:49:58 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /user
7:49:58 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext initialized in 4521 ms
Accessing WSDL File
You can access WSDL file using URL http://localhost:8080/soap-contract-first-webservice/services/user?wsdl in the browser.

Testing the Service
Creating Client CLass
Create a client class in to test the SOAP service.
package com.roytuts.soap.contract.first.webservice.client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import com.roytuts.soap.contract.first.User;
import com.roytuts.soap.contract.first.UserPort;
public class UserClient {
public static void main(String[] args) {
final String endpointAddress = "http://localhost:8080/soap-contract-first-webservice/services/user";
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(UserPort.class); // the SEI
factory.setAddress(endpointAddress);
UserPort port = (UserPort) factory.create();
User user = port.getUserDetailsRequest(2);
System.out.println(user.getId());
System.out.println(user.getName());
System.out.println(user.getEmail());
System.out.println(user.getAddress().getAddressType());
System.out.println(user.getAddress().getStreet());
System.out.println(user.getAddress().getCity());
System.out.println(user.getAddress().getState());
System.out.println(user.getAddress().getCountry());
System.out.println(user.getAddress().getZip());
}
}
Running Client Class
Run the client class you will get the output in console as shown below.
2
Loku
debabrata.poddar@email.com
COMMUNICATION
Birati
Kolkata
WB
India
700130
Source Code
Thanks for reading.