In this post I will describe the way to write a very simple SOAP-based web service that will take as input two numbers and return the sum of those numbers.
Create the Project Structure:
We can start our development based on a Java-Web project using maven. [ See maven web project directory layout and creating maven webapp project ].
Define Data Contract (XSD):
Spring-WS focuses on Contract-First development style [See Why]. So, we first define the data exchange (reuest-response) format of the web service, i.e. the Data Contract.
Generate Request/Response Models:
You can generate (un-marshal) Java classes using JAXB from the above XSD. You can use XJC for this or you can use features of your IDE to convert XML-to Java. In IntelliJ IDEA, right click on the .xsd file in the project explorer, click "Web Services" --> "Generate Java classes from XML Schema using JAXB". (for eclipse see this).
AddServiceRequest.java, AddServiceResponse.java and ObjectFactory.java will be generated in this case. Put the generated classes in a separate package (e.g. com.test.ws.wsmodels).
Then write the service interfaces and an implementation of the service interface.
Now write the endpoint class.
All the java classes are ready now. We need to configure the spring servlet definition and the web.xml file.
Now the maven configuration.
Now build the file using the maven command:
mvn clean package
Deploy the spring-ws-test.war file in your server (tomcat / glassfish / jboss). If you deploy the application in your localhost, then you can see the wsdl on your browser in the following URL:
http://localhost:8080/spring-ws-test/services/wstest.wsdl
The service can be tested using Soap-UI or other SOAP client.
You can get the whole project from github using git:
git clone https://github.com/tariqmnasim/spring-ws-test.git
References:
JAXB:
Create the Project Structure:
We can start our development based on a Java-Web project using maven. [ See maven web project directory layout and creating maven webapp project ].
Define Data Contract (XSD):
Spring-WS focuses on Contract-First development style [See Why]. So, we first define the data exchange (reuest-response) format of the web service, i.e. the Data Contract.
webapp/WEB-INF/spring/service-definition.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://develop-for-fun.blogspot.com/spring-ws" xmlns:wst="http://develop-for-fun.blogspot.com/spring-ws"> <xs:element name="AddServiceRequest"> <xs:complexType> <xs:sequence> <xs:element name="firstNumber" type="xs:integer"/> <xs:element name="secondNumber" type="xs:integer"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="AddServiceResponse"> <xs:complexType> <xs:sequence> <xs:element name="sum" type="xs:integer"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
Generate Request/Response Models:
You can generate (un-marshal) Java classes using JAXB from the above XSD. You can use XJC for this or you can use features of your IDE to convert XML-to Java. In IntelliJ IDEA, right click on the .xsd file in the project explorer, click "Web Services" --> "Generate Java classes from XML Schema using JAXB". (for eclipse see this).
AddServiceRequest.java, AddServiceResponse.java and ObjectFactory.java will be generated in this case. Put the generated classes in a separate package (e.g. com.test.ws.wsmodels).
Then write the service interfaces and an implementation of the service interface.
AddService.java
package com.test.ws.services; import java.math.BigInteger; public interface AddService { public BigInteger addNumbers(BigInteger number1, BigInteger number2); }
AddServiceImpl.java
package com.test.ws.services; import java.math.BigInteger; import org.springframework.stereotype.Service; @Service public class AddServiceImpl implements AddService { /** * Returns the sum of two input numbers. */ public BigInteger addNumbers(BigInteger number1, BigInteger number2) { return number1.add(number2); } }Don't forget the @service annotation.
Now write the endpoint class.
AdditionEndpoint.java
package com.test.ws.endpoints; 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.test.ws.wsmodels.*; import com.test.ws.services.AddService; @Endpoint public class AdditionEndpoint { //To calculate sum of two input. @Autowired private AddService addService; //This is similar to @RequestMapping of Spring MVC @PayloadRoot(localPart="AddServiceRequest", namespace="http://develop-for-fun.blogspot.com/spring-ws") @ResponsePayload public AddServiceResponse generateRandomNumber(@RequestPayload AddServiceRequest request) { AddServiceResponse response = new ObjectFactory().createAddServiceResponse(); response.setSum(addService.addNumbers(request.getFirstNumber(), request.getSecondNumber()) ); return response; } }
All the java classes are ready now. We need to configure the spring servlet definition and the web.xml file.
webapp/WEB-INF/spring/ws-servlet.xml
<?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:context="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd"> <sws:annotation-driven /> <!-- To detect @Service, @Endpoint etc --> <context:component-scan base-package="com.test.ws" /> <!-- To generate dynamic wsdl --> <sws:dynamic-wsdl id="wstest" portTypeName="add" locationUri="/services/add" targetNamespace="http://develop-for-fun.blogspot.com/spring-ws"> <sws:xsd location="/WEB-INF/spring/service-definitions.xsd" /> </sws:dynamic-wsdl> <!-- For validating your request and response --> <!-- So that you don't send a string instead of an integer --> <sws:interceptors> <bean id="validatingInterceptor" class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor"> <property name="schema" value="/WEB-INF/spring/service-definitions.xsd" /> <property name="validateRequest" value="true" /> <property name="validateResponse" value="true" /> </bean> </sws:interceptors> </beans>
webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <display-name>Addtion Service</display-name> <servlet> <servlet-name>ws</servlet-name> <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class> <init-param> <param-name>transformWsdlLocations</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/ws-servlet.xml </param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ws</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> </web-app>
Now the maven configuration.
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.test.ws</groupId> <artifactId>spring-ws-test</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>Spring-WS Application</name> <url>http://www.springframework.org/spring-ws</url> <build> <finalName>spring-ws-test</finalName> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxb2-maven-plugin</artifactId> <executions> <execution> <goals> <goal>xjc</goal> </goals> </execution> </executions> <configuration> <!-- This should the location of the schema files (*.xsd) for services definition --> <schemaDirectory>src/main/webapp/WEB-INF/spring</schemaDirectory> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core</artifactId> <version>2.0.1.RELEASE</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.0.3</version> </dependency> <dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <scope>compile</scope> <version>1.2.16</version> </dependency> </dependencies> </project>
Now build the file using the maven command:
mvn clean package
Deploy the spring-ws-test.war file in your server (tomcat / glassfish / jboss). If you deploy the application in your localhost, then you can see the wsdl on your browser in the following URL:
http://localhost:8080/spring-ws-test/services/wstest.wsdl
The service can be tested using Soap-UI or other SOAP client.
You can get the whole project from github using git:
git clone https://github.com/tariqmnasim/spring-ws-test.git
References:
JAXB: