Saturday, March 26, 2011

Testing Spring Web Service endpoint

In previous post we have covered very interesting approach to build SOAP web services using lightweight by very powerful Spring Web Services framework. To wrap it up, let me show how easy you can test your web services.

Let me start with Spring context for test case (which is 99% the copy-paste from previous post).



    

    
 
    

    
        
            
                
             
        
        
            
                
            
        
     
 
    

    
         

Let us save this context to /src/test/resources/META-INF/spring-context.xml. There are two minor differences (comparing to initial one):
  • element <ws:static-wsdl/> has been removed
  • element <bean id="schema" ... /> has been added
Having context prepared, let us move on to test case itself.
package org.example;

import static org.springframework.ws.test.server.ResponseMatchers.validPayload;
import static org.example.SoapActionRequestCreator.withPayload;

import java.io.IOException;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.ws.test.server.MockWebServiceClient;

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( locations = "/META-INF/spring-context.xml" )
public class UserProfileEndpointTestCase {
    @Autowired private ApplicationContext applicationContext;
    @Autowired private Resource schema;
 
    private MockWebServiceClient client; 

    @Before
    public void setUp() {
        client = MockWebServiceClient.createClient( applicationContext );
    }
   
    @Test
    public void testServiceCall() throws IOException {
        final Resource payload = applicationContext.getResource( "Request.xml" );
  
        client.sendRequest( withPayload( "UserProfile", request ) ).   
            andExpect( validPayload( schema ) ); 
    }
}
This particular example does send request to SOAP web service and ensures that response is valid (against XSD schema), all that by leveraging Spring Web Services test scaffolding. There is one class which requires a bit of explanation: org.example.SoapActionRequestCreator. Nevertheless Spring Web Services provides rich set of payload builders, I didn't find the one which allows to pass SOAP action into request. So this small utility class has been developed. Here is a code for it:
package org.example;

import java.io.IOException;

import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.test.server.RequestCreator;
import org.springframework.ws.test.support.creator.PayloadMessageCreator;
import org.springframework.ws.test.support.creator.WebServiceMessageCreator;
import org.springframework.xml.transform.ResourceSource;

public class SoapActionRequestCreator implements RequestCreator {
    private final WebServiceMessageCreator adaptee;
    private final String action;
 
    private SoapActionRequestCreator ( final String action, 
            final WebServiceMessageCreator adaptee ) {
        this.action = action;
        this.adaptee = adaptee;
    }

    public static RequestCreator withPayload( final String action, 
            final Resource payload )throws IOException {
        Assert.notNull(payload, "'payload' must not be null");
        return new SoapActionRequestCreator( 
            action, new PayloadMessageCreator( new ResourceSource( payload ) ) );
    }
   
    @Override
    public WebServiceMessage createRequest( 
            final WebServiceMessageFactory messageFactory ) throws IOException {
        final WebServiceMessage message = adaptee.createMessage( messageFactory );
        Assert.isInstanceOf( SoapMessage.class, message );

        if( message instanceof SoapMessage ) {
            ( ( SoapMessage )message ).setSoapAction( action );
        }
  
        return message;
    }
}
This is just a very basic example. There are a bunch of tests you can do to ensure your SOAP web service performs as expected. I encourage to explore Spring Web Services documentation. All that should help us to develop high quality code and be proud of it.

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.