Thursday, February 28, 2013

Spring-WS JAXB Java Webservice tutorial

This post shows how to create webservices using spring-ws. It took me a while to resolve numerous simple issues but I finally figured out how all this works.

I used "contract-first" approach by creating schema and wsdl first. Then, I created a Eclipse maven war type project, updated it with all required dependencies. One can take the reverse approach by exposing an EndPoint java class as a webservice and let Spring-ws publish wsdl dynamically.

I added a goal to generate Java code from schema that represents the payload of the webservice operations. 

I created a Java EndPoint class that will serve webservice requests.

Lastly, I created the spring webservices context file that defines all the webservice beans and marshaller/unmarshaller.

1. Define dispatcher servlet for Spring web services in  web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-application-config.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
<servlet-name>xyz-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>
</servlet>

<servlet-mapping>
<servlet-name>xyz-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

2. Create spring-ws context xml file to define web service 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: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 />

<context:component-scan base-package="com.xyz.abc.integration"
annotation-config="true" />

<bean id="XYZWebService"
class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<constructor-arg value="classpath:/XYZWebService.wsdl"/>
</bean>

<bean id="XYZSchema" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="classpath:/XYZSchema.xsd" />
</bean>

if you wanted Spring to generate wsdl automatically, comment out previous two lines and use this
<sws:dynamic-wsdl id="XYZWebService" portTypeName="XYZWebServicePort" locationUri="/xyzWebService/" targetNamespace="http://www.abc.com/XYZWebService/"> <sws:xsd location="classpath:/XYZSchema.xsd" /> </sws:dynamic-wsdl>

<bean id="marshallingPayloadMethodProcessor"
class="org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor">
<constructor-arg ref="marshaller" />
</bean>

<bean id="defaultMethodEndpointAdapter"
class="org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter">
<property name="methodArgumentResolvers">
<list>
<ref bean="marshallingPayloadMethodProcessor" />
</list>
</property>
<property name="methodReturnValueHandlers">
<list>
<ref bean="marshallingPayloadMethodProcessor" />
</list>
</property>
</bean>

<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.xyz.abc.integration.jaxb" />
</bean>

<bean id="exceptionResolver"
class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
<property name="defaultFault" value="SERVER" />
<property name="exceptionMappings">
<props>
<prop key="org.springframework.oxm.ValidationFailureException">CLIENT,Invalid</prop>
<prop key="javax.xml.bind.JAXBException">CLIENT,Invalid Request - Parser failed</prop>
</props>
</property>
</bean>

<bean id="xyzEndpoint" class="com.xyz.abc.integration.XYZEndpoint" scope="request">
<property name="someBusinessService" ref="someBusinessService"/>
</bean>

3. Create application context file to define other beans
<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:p="http://www.springframework.org/schema/p"
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-3.0.xsd">

<context:annotation-config/>

<bean id="someBusinessService" class="com.xyz.abc.integration.SomeBusinessServiceImpl" scope="request"/>

</beans>

4. Create xsd schemas for webservice input/output
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.xyz.com/schemas/abc"
elementFormDefault="qualified">

<xsd:element name="SendOrderRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Field11" type="xsd:string" maxOccurs="1" minOccurs="0"/>
<xsd:element name="Filed22" type="xsd:string" maxOccurs="1" minOccurs="0" />
<xsd:element name="Field33" type="xsd:string" maxOccurs="1" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>

<xsd:element name="SendOrderResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Field1" type="xsd:string" maxOccurs="1" minOccurs="0" />
<xsd:element name="Field2" type="xsd:string" maxOccurs="1" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>

</xsd:schema>

5. Create WSDL for service

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.abc.com/XYZWebService"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:rew="http://www.abc.com/schemas/XYZ"
name="XYZWebService" targetNamespace="http://www.abc.com/XYZWebService/">

<wsdl:types>
<xsd:schema targetNamespace="http://www.abc.com/XYZWebService/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://www.abc.com/schemas/XYZ"
schemaLocation="XYZSchema.xsd" />
</xsd:schema>
</wsdl:types>
<wsdl:message name="sendOrderRequest">
<wsdl:part element="rew:SendOrderRequest" name="sendOrderRequest" />
</wsdl:message>
<wsdl:message name="sendOrderResponse">
<wsdl:part element="rew:SendOrderResponse" name="sendOrderResponse" />
</wsdl:message>

<wsdl:portType name="XYZWebService">
<wsdl:operation name="SendOrder">
<wsdl:input message="tns:sendOrderRequest" name="SendOrderRequest" />
<wsdl:output message="tns:sendOrderResponse" name="SendOrderResponse" />
</wsdl:operation>
</wsdl:portType>

<!-- Do we need to define soapAction -->
<wsdl:binding name="XYZWebServiceSOAP" type="tns:XYZWebService">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />

<wsdl:operation name="SendOrder">
<soap:operation soapAction="" />
<wsdl:input name="SendOrderRequest">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="SendOrderResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="XYZWebService">
<wsdl:port binding="tns:XYZWebServiceSOAP" name="XYZWebServiceSOAP">
<soap:address location="http://localhost:7001/XYZService/ws" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>


6. Create Endpoint java class
@Endpoint
public class XYZEndpoint {

private static final Logger LOG = Logger.getLogger(XYZEndpoint.class);

private static final String NAMESPACE_URI = "http://www.xyz.com/schemas/xyz";


private SomeBusinessService someBusinessService;

public SomeBusinessService getSomeBusinessService() {
return this.someBusinessService;
}

public void setSomeBusinessService(SomeBusinessService someBusinessService) {
this.someBusinessService = someBusinessService;
}

@PayloadRoot(localPart = "SendOrderRequest", namespace = NAMESPACE_URI)
@ResponsePayload
public SendOrderResponse sendOrder(@RequestPayload SendOrderRequest request, SoapHeader header)
{
LOG.info("in sendOrder.....");
com.xyz.abc.integration.jaxb.ObjectFactory of = new com.xyz.abc.integration.jaxb.ObjectFactory();
SendOrderResponse resp = of.createSendOrderResponse();

resp.setField1(request.getField11());
return resp;
}

7. maven pom
<dependencies>
<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>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>XYZService</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>com.sun.tools.xjc.maven2</groupId>
<artifactId>maven-jaxb-plugin</artifactId>
<version>1.1.1</version>
<configuration>
<generatePackage>com.xyz.abc.integration.jaxb</generatePackage>
<schemaDirectory>src\main\resources</schemaDirectory>
<generateDirectory>src\main\java</generateDirectory>
<removeOldOutput>true</removeOldOutput>
<includeSchemas>
<includeSchema>*.xsd</includeSchema>
</includeSchemas>
<includeBindings>
<includeBinding>*.xjb</includeBinding>
</includeBindings>
<strict>false</strict>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

8. WSDL and XSD location

http://localhost:7001/XYZService/XYZWebService.wsdl
http://localhost:7001/XYZService/XYZSchema.xsd

9. SOAP UI 
http://localhost:7001/XYZService

1 comment:

victor said...

hi,

I am trying to make your example but it give me some problems could you upload the entire example please.

thanks