Monday, March 21, 2011

Get hands on Spring Web Services

Suppose you are Java web developer. Suppose you are deploying all your stuff on Apache Tomcat. Suppose you are about to develop some web services. There are number of choices you can do that (including Apache Axis 2). But one I would like to describe today is ... Spring Web Services project.

I will omit the web service contract design phase and assume we are developing SOAP web service and WSDL (or XSD schema) is already available. The best way (in my opinion) to generate Java model from WSDL is to use JAXB2 specification implementation. There are several ways to get it done:
- using xjc compiler
- using Maven 2.x/3.x plugin (I lean towards this one and use it all the time)

    org.jvnet.jaxb2.maven2
    maven-jaxb2-plugin
    0.7.5
    
        WSDL
        src/main/resources
        
            */*.wsdl
             
    


Ok, so we have our Java classes (with JAXB2 annotations), generated from our WSDL. As picture describes, we have UserProfileService web service with UserProfile operations which accepts UserProfileRequest as input and returns UserProfileResponse. In case of exception, UserProfileFault has been returned.
Let's do some routine work in order to configure Spring application context with Spring Web Services specific beans: message factory, marshallers and endpoint processors.



    
    

    
 
    

    
        
            
                
             
        
        
            
                
            
        
     
 
    


And here is how our web service endpoint looks like:
package org.example;

import javax.xml.bind.JAXBElement;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.server.endpoint.annotation.SoapAction;

@Endpoint
public class UserProfileEndpoint {
    @SoapAction( "UserProfile" )
    public @ResponsePayload JAXBElement< UserProfileResponse > getUserProfile(
           @RequestPayload JAXBElement< UserProfileRequest > request ) {
  ...
 }
}
There are a few important things to mention:
- @Endpoint annotation tells Spring Web Services that we have web service endpoint
- @SoapAction annotation (on the methods) tells Spring Web Services that method in question is responsible to handle particular SOAP action
- @ResponsePayload and @RequestPayload annotations tell Spring Web Services to extract payload from SOAP message, deserialize it to Java classes (using JAXB2 binding), and match input/output parameters against declared handlers (methods)

That's pretty much it to make things work! Spring Web Services takes care about all boiler-plate code and allows to concentrate on what really matters - implementation.

So, web service is up and running. Aside from very basic features, Spring Web Services allows to configure response/request validation (against XSD schema). To do that, we need just to add validation interceptor to the application context:
       
    
        
        
        
     

Also, Spring Web Services allows to define our own SOAP fault handlers (based on exception pattern) and much more! For example, if there is a need to add some details to SOAP fault in case of any exception, here is the way to do that:
package org.example;

import org.springframework.stereotype.Component;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver;

@Component
public class UserProfileEndpointExceptionResolver extends SimpleSoapExceptionResolver {
    public UserProfileEndpointExceptionResolver() {
        super();

        // Let this handler precede all other handlers
        super.setOrder( HIGHEST_PRECEDENCE );
    }

    @Override
    protected void customizeFault( MessageContext messageContext, Object endpoint, Exception ex, SoapFault fault ) {
       // Customize here your SOAP fault with some details
    }
}
That's it. In next post I would like to share how to write JUnit tests and ensure your web service works as expected.

3 comments:

karan said...

Hi Andriy,

Thanks alot for this information. I Found it extremely useful.

Now I need to be able to set the security headers(username Token) in the MockWebServiceClient.
As the web service interceptor(XwsSecurityInterceptor) checks for the username token.

please help.

thanks
jaskaran
(jaskaran81@yahoo.com)

karan said...

hi i was able to fix my issue with help from:-
http://stackoverflow.com/questions/2274378/add-soapheader-to-org-springframework-ws-webservicemessage

Andriy Redko said...

Hi Karan,

Thank you a lot for sharing such a very useful information!