Thursday 1 November 2012

Developing a contract-first Spring Web Services


  ***Contract-first Spring Web Services***

There are two approaches: Contract Last and Contract First for develping web services.
We can easily develop contract first web service using Spring Web Services. It focus more on XML and lesser on Java implementation.

When we use contract-first approach, we start with the web services contract (XML files like wsdl, xsd)
and after that we are proceeding with the Java impletement the web service contract.
When we use the contract-last approach, we first write the Java code and after web service contract(WSDL) to be genereated for that. Spring Web Services only supports the contract-first development style.

Here we will design a sample application for getting the factorial of the given number using Spring Web Service. Given below are the pre-requisite softwares to develop and run the application

•JDK 1.6 or higher
•Spring Web Service jars
•Web Server(Like Tomcat 6.0 or Tomcat 7.0)


The following steps are required to develop the sample application:

1.Contract Design or writing the XSD file


2.Defining the service interface for getting the factorial


3.Implementing the service interface


4.Defining Spring Web-Service endpoints


5.Configuring deployment descriptor.


6.Configuring Spring’s Application Context file.


STEPS

1) Create a web project in Eclipse and name it as find-factorial-using-contractfirst.




2) Writing the XSD file for the Factorial of a given number

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified" attributeFormDefault="qualified"
    targetNamespace="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service"
    xmlns:tns="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service">

    <element name="findFactorialRequest" type="string" />
    <element name="findFactorialResponse" type="string" />

</schema>


3) Writing the service interface for factorial service
  •  Create a package name as gaurav.springws.findfactorial.
  • Create a java class name as FindFactorialService.java
  • Write the code as below in this class :
FindFactorialService.java

package gaurav.springws.findfactorial;
public interface FindFactorialService {
     String getFactorial(String anyString);
}


4) Writing the service interface implementation
  •  Create a class FindFactorialServiceImpl.java in gaurav.springws.findfactorial package.
  • Write the below code in the impl java file:
FindFactorialServiceImpl.java

package gaurav.springws.findfactorial;

public class FindFactorialServiceImpl implements FindFactorialService {

    public boolean isParsableToInteger(String strValue) {
        try {
            Integer.parseInt(strValue);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    public String getFactorial(String inputString) {

        String factorialResult = "Not a Valid Number";

        if (inputString == null || inputString.trim().length() == 0) {
            return "";
        }

        if (isParsableToInteger(inputString)) {

            int number = Integer.parseInt(inputString);

            int factorial = number;

            for (int i = (number - 1); i > 1; i--) {
                factorial = factorial * i;
            }
            factorialResult = String.valueOf(factorial);

        }

        return factorialResult;
    }
}


5) Writing Spring Web-Service endpoints
  •  Create a package and name it as gaurav.springws.findfactorial.endpoint.
  •  Create a class and name it as FindFactorialServiceEndPoint.java
  •  Write the below code in the EndPoint file.


FindFactorialServiceEndPoint.java

package gaurav.springws.findfactorial.endpoint;

import gaurav.springws.findfactorial.FindFactorialService;

import org.springframework.ws.server.endpoint.AbstractDomPayloadEndpoint;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

public class FindFactorialServiceEndPoint extends AbstractDomPayloadEndpoint {

    public static final String NAMESPACE = "http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service";

    private FindFactorialService findFactorialService;

    public void setFindFactorialService(
            FindFactorialService findFactorialService) {
        this.findFactorialService = findFactorialService;
    }

    protected Element invokeInternal(Element requestElement, Document document)
            throws Exception {

        String requestString = findRequestString(requestElement);
        String findFactorialString = invokeServiceAndReturnResponse(requestString);
        Element responseXml = prepareResponseXml(document, findFactorialString);
        return responseXml;
    }

    private static String findRequestString(Element requestElement) {

        NodeList childNodes = requestElement.getChildNodes();
        String requestString = null;
        for (int i = 0; i < childNodes.getLength(); i++) {
            if (childNodes.item(i).getNodeType() == Node.TEXT_NODE) {
                Text tempText = (Text) childNodes.item(i);
                requestString = tempText.getNodeValue();
                break;
            }
        }
        return requestString;
    }

    private String invokeServiceAndReturnResponse(String requestString) {

        String findFactorialString = findFactorialService
                .getFactorial(requestString);
        return findFactorialString;
    }

    private static Element prepareResponseXml(Document document,
            String responseString) {

        Element responseElement = document.createElementNS(NAMESPACE,
                "findFactorialResponse");
        Text responseText = document.createTextNode(responseString);
        responseElement.appendChild(responseText);
        return responseElement;
    }
}

 

6) Writing the web.xml file, code is given below :

<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>find-factorial-using-contractfirst</display-name>
  <servlet>
        <servlet-name>spring-contractfirst-webservice</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>spring-contractfirst-webservice</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>


7) Writing the spring-contractfirst-webservice-servlet.xml file. In this file, we will define the location, name, schema Type, Endpoint for the factorial service.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <bean id="findFactorialService" class="gaurav.springws.findfactorial.FindFactorialServiceImpl">
    </bean>

    <bean id="findFactorialServiceEndpoint"
        class="gaurav.springws.findfactorial.endpoint.FindFactorialServiceEndPoint">
        <property name="findFactorialService" ref="findFactorialService" />
    </bean>

    <bean id="payloadMapping"
        class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
        <property name="defaultEndpoint" ref="findFactorialServiceEndpoint" />
    </bean>

    <bean id="findFactorialSchema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="/WEB-INF/findFactorialService.xsd" />
    </bean>

    <bean id="findFactorial"
        class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="findFactorialSchema" />
        <property name="portTypeName" value="findFactorial" />
        <property name="locationUri"
            value="http://localhost:7070/find-factorial-using-contractfirst/services" />
    </bean>


</beans>


8) The required jars for this project is shown below:-



9) The final architecture of the project is as below:-


10) Select the application, Right Click on it, go to Run As option and select Run On Server. Configure the Tomcat server 6.0 or 7.0, on the port 7070, as I am running my tomcat 6.0 on the port 7070.

11) After running the server, hit the below URL:
http://localhost:7070/find-factorial-using-contractfirst/services/findFactorial.wsdl 

Note :--> http://localhost:7070/find-factorial-using-contractfirst/services - This is the Location URI mentioned in spring-contractfirst-webservice-servlet.xml file. and findFactorial is the portTypeName.

12) After hitting the above URL the WSDL which we will get is shown below:

 <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
- <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service" targetNamespace="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service">
- <wsdl:types>
- <schema xmlns="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service">
  <element name="findFactorialRequest" type="string" />
  <element name="findFactorialResponse" type="string" />
  </schema>
  </wsdl:types>
- <wsdl:message name="findFactorialRequest">
  <wsdl:part element="tns:findFactorialRequest" name="findFactorialRequest" />
  </wsdl:message>
- <wsdl:message name="findFactorialResponse">
  <wsdl:part element="tns:findFactorialResponse" name="findFactorialResponse" />
  </wsdl:message>
- <wsdl:portType name="findFactorial">
- <wsdl:operation name="findFactorial">
  <wsdl:input message="tns:findFactorialRequest" name="findFactorialRequest" />
  <wsdl:output message="tns:findFactorialResponse" name="findFactorialResponse" />
  </wsdl:operation>
  </wsdl:portType>
- <wsdl:binding name="findFactorialSoap11" type="tns:findFactorial">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
- <wsdl:operation name="findFactorial">
  <soap:operation soapAction="" />
- <wsdl:input name="findFactorialRequest">
  <soap:body use="literal" />
  </wsdl:input>
- <wsdl:output name="findFactorialResponse">
  <soap:body use="literal" />
  </wsdl:output>
  </wsdl:operation>
  </wsdl:binding>
- <wsdl:service name="findFactorialService">
- <wsdl:port binding="tns:findFactorialSoap11" name="findFactorialSoap11">
  <soap:address location="http://localhost:7070/find-factorial-using-contractfirst/services" />
  </wsdl:port>
  </wsdl:service>
  </wsdl:definitions>

13) After getting the WSDL, Use this http://localhost:7070/find-factorial-using-contractfirst/services/findFactorial.wsdl  URL in SoapUI for Test purpose - I am using SoapUI 3.0.1.
  • Go to File, select New SoapUI Project, put the above URL in Initial WSDL/WADL option.
  • After placing this URL, it will automatically create a request like below:

Request:-

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:fin="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service">
   <soapenv:Header/>
   <soapenv:Body>
      <fin:findFactorialRequest>?</fin:findFactorialRequest>
   </soapenv:Body>
</soapenv:Envelope>


Note:- Pass the number in place of question mark.( I passed 5 in place of Question Mark).

Response:- 

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <findFactorialResponse xmlns="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service">120</findFactorialResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>


*****Note:- Pass some invalid number like 6.5. The response will come like below:-

For INVALID Condition:-

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <findFactorialResponse xmlns="http://localhost:7070/gaurav/springws/findfactorial/findfactorial-service">Not a Valid Number</findFactorialResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope> 


 

No comments:

Post a Comment