Search This Blog

Thursday, 20 December 2012

SOAP Webservices using Spring -3

As of now we have configured the web service and have also made our contract (or WSDL ) visible to the client. But what happens when the client actually sends a request? How do we handle it?
Just like in spring-mvc we write controllers to process requests similarly in spring-ws we create endpoints.
Consider the endpoint to handle our client requests:
@Endpoint
public class RandomOperationsEndPoint {

    private static final Logger logger = Logger.getLogger(RandomOperationsEndPoint.class);
    private static final String NAMESPACE_URI = "http://ws.com/Service/xsd/random-schema";
    
    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "GetRandomRequest")
    @ResponsePayload
    public GetRandomResponse handleRequest(
         @RequestPayload final GetRandomRequest activateServiceRequest)
         throws Exception {
        String time = new DateFormatter().print(new Date(),Locale.ENGLISH);
        String name = activateServiceRequest.getName();
        logger.info("received request at " + time + " for name " + name);
        GetRandomResponse getRandomResponse = new GetRandomResponse();
        getRandomResponse.setValue(5);
        return getRandomResponse;
    }
}
The first thing of note is the @Endpoint annotation. The annotation like the @Controller annotation makes the class eligible to be instantiated as a bean (if using component -scan). It also indicates that the class is capable of handling XML messages in spring-ws.
Next is the @PayloadRoot annotation. This is added on a method. It tells Spring which type of requests can be processed by this method. The handleRequest method is capable of handling requests where the part in the received message id of type GetRandomRequest. The namespace attribute is the namespace of the part.
This is an example of annotation based mapping of handlers or PayloadRootAnnotationMethodEndpointMapping.
The actual code inside the method does nothing but return 5. It does not even care for the request details.
The other two annotations - @RequestPayload and @ResponsePayload are used for unmarshaling and marshaling the request and response objects.
I used the WebService Explorer provided within eclipse to test my webservice.
Sample Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:q0="http://ws.com/Service/xsd/random-schema" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <q0:GetRandomRequest>
            <q0:name>test</q0:name>
        </q0:GetRandomRequest>
    </soapenv:Body>
</soapenv:Envelope>
The response returned was:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <ns2:GetRandomResponse xmlns:ns2="http://ws.com/Service/xsd/random-schema">
            <ns2:value>5</ns2:value>
        </ns2:GetRandomResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>  
The logs of the execution is as below:
DEBUG org.springframework.ws.server.MessageTracing.received  - Received request 
[<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
 ...//the actual request in xml format (displayed above) 
</soapenv:Envelope>]
DEBUG org.springframework.ws.soap.saaj.support.SaajUtils  - SOAPElement 
[com.sun.xml.internal.messaging.saaj.soap.ver1_1.Body1_1Impl] implements SAAJ 1.3
DEBUG org.springframework.ws.server.endpoint.mapping.
PayloadRootAnnotationMethodEndpointMapping  - Looking up endpoint for 
[{http://ws.com/Service/xsd/random-schema}GetRandomRequest]
DEBUG org.springframework.ws.soap.server.SoapMessageDispatcher  
- Endpoint mapping [org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping@e07e6b] 
maps request to endpoint [public com.ws.service.xsd.random_schema.GetRandomResponse com.ws.endpoint
.RandomOperationsEndPoint.handleRequest(com.ws.service.xsd.random_schema.GetRandomRequest) throws java.lang.Exception]
DEBUG org.springframework.ws.soap.server.SoapMessageDispatcher  
- Testing endpoint adapter [org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter@1a0d111]
DEBUG org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter  
- Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.
MessageContextMethodArgumentResolver@fdf48d] supports [class com.ws.service.xsd.random_schema.GetRandomRequest]
...
DEBUG org.springframework.ws.server.endpoint.adapter.method.jaxb.
XmlRootElementPayloadMethodProcessor  - Unmarshalled payload request to 
[com.ws.service.xsd.random_schema.GetRandomRequest@84850]
DEBUG org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter  
- Invoking [public com.ws.service.xsd.random_schema.GetRandomResponse com.ws.endpoint
.RandomOperationsEndPoint.handleRequest(com.ws.service.xsd.random_schema.GetRandomRequest) 
throws java.lang.Exception] with arguments [com.ws.service.xsd.random_schema.GetRandomRequest@84850]
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  
- Returning cached instance of singleton bean 'randomOperationsEndPoint'

INFO  com.ws.endpoint.RandomOperationsEndPoint  
- received request at Sep 3, 2012 for name test
As can be seen:
  1. The request was received at the server as XML(SOAP).
  2. Based on the message part and its namespace the PayloadRootAnnotationMethodEndpointMapping class attempts to identify the Endpoint to handle this request.
  3. After successful identification of an endpoint it verifies that the message body can be resolved into the request parameter of the Endpoint method. It then proceeds to unmarshal the received request.
  4. The call is now delegated from the servlet to the Endpoint method. as can be seen our log has been printed.
Now to checks the logs generated after our method was executed:
DEBUG org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter  
- Method [public com.ws.service.xsd.random_schema.GetRandomResponse com.ws.endpoint.
RandomOperationsEndPoint.handleRequest(com.ws.service.xsd.random_schema.
GetRandomRequest) throws java.lang.Exception] 
returned [com.ws.service.xsd.random_schema.GetRandomResponse@17fc1e7]
DEBUG org.springframework.ws.server.endpoint.adapter.method.jaxb.
XmlRootElementPayloadMethodProcessor  - Marshalling [com.ws.service.xsd.
random_schema.GetRandomResponse@17fc1e7] to response payload
DEBUG org.springframework.ws.server.MessageTracing.sent  - 
Sent response [<SOAP-ENV:Envelope xmlns:SOAP-ENV="
http://schemas.xmlsoap.org/soap/envelope/">
//The actual response
</soapenv:Envelope>]
DEBUG org.springframework.ws.transport.http.MessageDispatcherServlet  - 
Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@8ab721
DEBUG org.springframework.ws.transport.http.MessageDispatcherServlet  
- Successfully completed request
DEBUG org.springframework.web.context.support.XmlWebApplicationContext  
- Publishing event in WebApplicationContext for namespace 'spring-ws-servlet': 
ServletRequestHandledEvent: url=[/sampleWS/Service/ws-operations]; 
client=[127.0.0.1]; method=[POST]; 
servlet=[spring-ws]; session=[null]; user=[null]; time=[844ms]; status=[OK]
The logs indicate
  1. On completion of endpoint the control returned to the servlet.
  2. The received method response was then marshaled using jaxb to an XML.
  3. The response was then written back to the stream
If we want to handle an exception in the code we simply need to create a fault and return it.

5 comments:

  1. This is really wonderfull blog post. I noticed this blog further more useful information. Thanks for sharing your useful views..... Web Services in Lucknow

    ReplyDelete
  2. Can you share this project code??

    I can use it as practice

    ReplyDelete
  3. Can you please share the source code?. it's some what unclear without source code.

    ReplyDelete
  4. Hello, Thanks for this info, but it's not quite understanding what you're saying. Could you please share your sample code ASAP? Thanks !

    ReplyDelete
  5. Hi, thanks for the tutorial,

    I don't understand, don't you need a spring-ws-servlet.xml ?

    ReplyDelete