Spring web services a product of spring community, helps in developing web services using contract first web services and helps in manipulating the xml in many ways. In this tutorial I am about to show a step by step guide in developing spring web services using JAXB as mashalling technology.
You can download spring, spring web services
here.
We are about to develop a web service which search the blogs based on a search criteria on title string and published date.
So lets get started,
1.Create a web application
This is a general spring web MVC application. for a skeleton application, you can just run this maven archetype to quickly start with,
mvn archetype:create -DarchetypeGroupId=org.springframework.ws \
-DarchetypeArtifactId=spring-ws-archetype \
-DarchetypeVersion=1.5.4 \
-DgroupId=com.teja \
-DartifactId=spring-ws-test
Or you can just add the spring nature to your existing web application by configuring the web.xml shown in the next step. Here is the file structure I am using for this simple demo.
2.Configure web.xml.
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>*.wsdl</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>*.soap</url-pattern>
</servlet-mapping>
Spring strongly supports only contract first web services for several reasons like Fragility, performance, re usability and versioning.
You can find more information visit this
linkSo lets start first by developing our XSD....
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://com.tejakantamneni/schemas"
xmlns:tns="http://com.tejakantamneni/schemas"
elementFormDefault="qualified">
</xsd:schema>
This is a plain regular xsd where we will define all our xml elements going to create our wsdl file.
Lets define our blog object, the blog is just a complex type object with three properties the "Title" for the blog, the "AbstractDetail" of the blog and the date when the blog is published "PublishedDate". This can be defined in the xml complex type
as follows...
<xsd:complexType name="Blog">
<xsd:sequence>
<xsd:element name="Title" type="xsd:string" nillable="true"/>
<xsd:element name="AbstractDetail" type="xsd:string" nillable="true"/>
<xsd:element name="PublishedDate" type ="xsd:dateTime" nillable="true"/>
</xsd:sequence>
</xsd:complexType>
Next step is defining the search criteria object, I am wrapping the search criteria into a seperate object so that in future, we can extend the search criteria by adding more elements like author, subject. Here the xsd definition of the search criteria
object with two fields title and published date.
<xsd:element name="SearchCriteria">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Title" type="xsd:string" nillable="true"/>
<xsd:element name="PublishedDate" type ="xsd:dateTime" nillable="true"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
The next stepa are creating the actual request object for our web service, which is basically a wrapper on the search criteria and our response object which is just a array/list of blog objects (which can be either zero size, if search results nothing
or can be of size n if search returns n objects).
Here is our completed xsd,
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://com.tejakantamneni/schemas"
xmlns:tns="http://com.tejakantamneni/schemas"
elementFormDefault="qualified">
<xsd:element name="BlogSearchRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:SearchCriteria" minOccurs="0" maxOccurs = "unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="BlogSearchResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="blogList" type="tns:Blog" minOccurs="0" maxOccurs = "unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="SearchCriteria">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Title" type="xsd:string" nillable="true"/>
<xsd:element name="PublishedDate" type ="xsd:dateTime" nillable="true"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="Blog">
<xsd:sequence>
<xsd:element name="Title" type="xsd:string" nillable="true"/>
<xsd:element name="AbstractDetail" type="xsd:string" nillable="true"/>
<xsd:element name="PublishedDate" type ="xsd:dateTime" nillable="true"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
The next in developing our web services is defining our contract, the WSDL file.
Here is the complete version the WSDL file.
Essentilly we are improting the xsd file which we had created in the earlier step to use them in the wsdl
Then create the message parts, the port and the binding operation for the blog search service. This is self explanatory.
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="BlogSearch" targetNamespace="http://com.tejakantamneni/definitions"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://com.tejakantamneni/definitions"
xmlns:schema="http://com.tejakantamneni/schemas">
<wsdl:types>
<xsd:schema targetNamespace="http://com.tejakantamneni/definitions" xmlns:schema="http://com.tejakantamneni/schemas">
<xsd:import namespace="http://com.tejakantamneni/schemas" schemaLocation="../schemas/BlogSearch.xsd"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="BlogSearchRequest">
<wsdl:part name="BlogSearchRequest" element="schema:BlogSearchRequest"/>
</wsdl:message>
<wsdl:message name="BlogSearchResponse">
<wsdl:part name="BlogSearchResponse" element="schema:BlogSearchResponse"/>
</wsdl:message>
<wsdl:portType name="BlogSearchService">
<wsdl:operation name="BlogSearchOperation">
<wsdl:input message="tns:BlogSearchRequest" name="BlogSearchRequest" />
<wsdl:output message="tns:BlogSearchResponse" name="BlogSearchResponse" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="BlogSearchBinding" type="tns:BlogSearchService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="BlogSearchOperation">
<soap:operation soapAction="http://tejakantamneni.com/SearchBlogs"/>
<wsdl:input name="BlogSearchRequest">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="BlogSearchResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="BlogSearchService">
<wsdl:port binding="tns:BlogSearchBinding" name="BlogSearchBindingPort">
<soap:address location="http://localhost:8080/spring-ws-test/BlogSearch.soap"/>
</wsdl:port>
</wsdl:service>
</definitions>
next step is creating the JAXB objects for your xsd elements using xjc compiler. you can either craft the JAXB objects by hand or just
use the xjc compiler. I prefer the later one. see https://jaxb.dev.java.net/guide/ for more details. (My Intellij Idea supports generating JAXB objects directly from xsd).
(Posting all the code is not practical, So I am moving here ahead posting the important parts)
Here is the endpoint
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.teja.service.endpoint;
import com.teja.Blog;
import com.teja.BlogSearchRequest;
import com.teja.BlogSearchResponse;
import com.teja.SearchCriteria;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.support.MarshallingSource;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.XPathParam;
import org.springframework.ws.soap.server.endpoint.annotation.SoapAction;
/**
* @author Teja Kantamneni
* @version 1.0 Aug 25, 2008 - 11:30:49 AM
*/
@Endpoint
public class BlogSearchEndpoint {
public static final String BLOG_SEARCH_ACTION =
"http://tejakantamneni.com/SearchBlogs";
@Autowired
Marshaller marshaller;
@SoapAction(BLOG_SEARCH_ACTION)
// public Source searchBlogs(@XPathParam("/sch:BlogSearchRequest/sch:SearchCriteria/sch:Title") String title, @XPathParam("/sch:BlogSearchRequest/sch:SearchCriteria/sch:PublishedDate") String publishedDate) throws DatatypeConfigurationException {
BlogSearchResponse blogSearchResponse = new BlogSearchResponse();
List<Blog> resultBlogs = new ArrayList<Blog>();
gregorianCalendar.
setTime(new Date());
XMLGregorianCalendar xmlGregorianCalendar = datatypeFactory.
newXMLGregorianCalendar(gregorianCalendar
);
Blog b = new Blog();
b.setAbstractDetail("Abstract details -teja");
b.setTitle("Teja test");
b.setPublishedDate(xmlGregorianCalendar);
resultBlogs.add(b);
for (Blog blog : resultBlogs) {
blogSearchResponse.getBlogList().add(blog);
}
return blogSearchResponse;
}
}
now the last and more important step, putting all the things together to deploy the service,configuring the spring-ws-servlet.xml
First and the most important point here is I am autowiring the endpoint by using the annotation @Endpoint.
So in the context.xml, we are defining the wsdl to be exposed as a bean using the class org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition.
I am specifying the JAXBMarshaller and injecting all the jaxb classes.
I am also configuring a couple of beans for validating and logging each and every request and response.
<?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:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<bean id="BlogSearch" class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition"
p:wsdl="/wsdls/BlogSearch.wsdl"/>
<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter" >
<constructor-arg ref="marshaller" index="0"/>
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.teja.Blog</value>
<value>com.teja.BlogSearchRequest</value>
<value>com.teja.BlogSearchResponse</value>
<value>com.teja.SearchCriteria</value>
</list>
</property>
<property name="schema" value="/schemas/BlogSearch.xsd" />
</bean>
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="interceptors">
<list>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
<ref bean="validatingInterceptor"/>
</list>
</property>
</bean>
<bean class="org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationMethodEndpointMapping">
<property name="interceptors">
<list>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
<ref bean="validatingInterceptor"/>
</list>
</property>
</bean>
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor"
p:schema="/schemas/BlogSearch.xsd"
p:validateRequest="true"
p:validateResponse="true"/>
<bean class="org.springframework.ws.server.endpoint.adapter.XPathParamAnnotationMethodEndpointAdapter">
<property name="namespaces">
<props>
<prop key="sch">http://com.tejakantamneni/schemas</prop>
<prop key="xs">http://www.w3.org/2001/XMLSchema</prop>
</props>
</property>
</bean>
<!-- scan classes for dependency annotations -->
<context:component-scan base-package="com.teja"/>
<context:annotation-config/>
<bean class="org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver">
<property name="order" value="2"/>
</bean>
</beans>
you can access the wsdl at http://localhost:8080/spring-ws-test/wsdl/BlogSearch.wsdl