I was tasked to implement read timeouts in my Spring webservices. I need to be able to implement this on dozens of webservices fairly easy and be maintainable. I also wanted to use Groovy Spring DSL for my bean declarations, and have my timeout configurable, and Integration testable.
I started with JaxWsPortProxyFactoryBean and this worked great
routingLookupService(org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean) {
serviceInterface = "com.comcast.ivr.das.services.RoutingLookupServicePortType"
wsdlDocumentUrl = ConfigurationHolder.config.routingLookupService.wsdlDocumentUrl
namespaceUri = "http://services.das.ivr.comcast.com"
serviceName = "RoutingLookupService"
endpointAddress = ConfigurationHolder.config.routingLookupService.endpointAddress
maintainSession = "false"
}
But I was not able to set a Read Timeout on the JaxWsPortProxyFactoryBean implementation.
I started reading this blog:
http://onebyteatatime.wordpress.com/2009/03/19/how-to-set-socket-timeout-using-spring-webservicetemplate/ and this helped get me in to correct direction.
1st I wanted to use Groovy Spring DSL instead of XML syntax, but after converting the applicationContext, I immediately ran into an issue with my JAXB generated Request Objects not having the correct @XmlRootElement annotations in it, so I needed to take a slighty different approach.
I did take some insight from this Blog:
http://javaandjava.blogspot.com/2008/12/using-jaxb-in-spring-maven-environment.html
and came up with this Groovy Spring DSL using WebServiceTemplate
routingLookupService(org.springframework.ws.client.core.WebServiceTemplate) {
messageSenders = [ref("routingLookupHttpSender")]
marshaller = ref("marshaller")
unmarshaller = ref("marshaller")
messageFactory = ref("axiomMessageFactory")
defaultUri = ConfigurationHolder.config.routingLookupService.endpointAddress
}
routingLookupHttpClient(org.apache.commons.httpclient.HttpClient){
params = ref(
httpParams(org.apache.commons.httpclient.params.HttpClientParams){
// Timeout in milliseconds: in this case 2 minutes (120 000)
soTimeout = ConfigurationHolder.config.routingLookupService.timeout
}
)
}
routingLookupHttpSender(org.springframework.ws.transport.http.CommonsHttpMessageSender,
ref("routingLookupHttpClient")){
}
routingLookupServiceClient(com.comcast.ivr.das.domain.routinglookup.xsd.RoutingLookupServiceClient){
messageSenders = [ref("routingLookupHttpSender")]
}
//--- Common Webservices Beans ---//
messageFactory(org.springframework.ws.soap.saaj.SaajSoapMessageFactory)
axiomMessageFactory(org.springframework.ws.soap.axiom.AxiomSoapMessageFactory){
payloadCaching = "true"
soapVersion = org.springframework.ws.soap.SoapVersion.SOAP_12
}
marshaller(org.springframework.oxm.jaxb.Jaxb2Marshaller){
contextPaths = ["com.comcast.ivr.das.services"]
marshallerProperties = ["jaxb.formatted.output": true]
}
In my RoutingLookupClient, I then extended WebServiceGatewaySupport so that I could…
public class RoutingLookupServiceClient extends WebServiceGatewaySupport {
@Resource
private WebServiceTemplate routingLookupService;
/**
* Get UIVR Lookup Routing
* com.comcast.ivr.das.services.UivrLookupRoutingResponse
*
* @param request {@link UivrRoutingLookupRequest}
* @param attributeProperties {@link List<AttributeProperty>}
* @return {@link RoutingLookupResponse}
* @throws RoutingLookupServiceClientException
* for any Webservice Fault
*/
public RoutingLookupResponse uivrLookupRouting(UivrRoutingLookupRequest request, List<AttributeProperty> attributeProperties)
throws RoutingLookupServiceClientException {
try {
(request.getAttributePropertyList()).addAll(attributeProperties);
final UivrLookupRouting uivrLookupRouting = new ObjectFactory().createUivrLookupRouting();
uivrLookupRouting.setRoutingLookupRequest(request);
return ((UivrLookupRoutingResponse) routingLookupService.marshalSendAndReceive(uivrLookupRouting, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {
((AxiomSoapMessage) webServiceMessage).setSoapAction(routingLookupService.getDefaultUri());
}
})).getReturn();
} catch (org.springframework.ws.soap.client.SoapFaultClientException e) {
throw new RoutingLookupServiceClientException();
} catch (WebServiceIOException e) {
throw new RoutingLookupServiceClientException();
}
}
And my Integration JUnit test to simulate a timeout:
public void testRoutingLookupServiceClientTimeout(){
try{
routingLookupHttpSender.readTimeout = 2
def RoutingLookupResponse routingLookupResponse = routingLookupServiceClient.uivrLookupRouting(request, attributeProperties);
fail()
}
catch(RoutingLookupServiceClientException e){
println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
println(e)
assertTrue(true)
}
}
Next Steps
I have dozens of Webservices to create, and each will have their own timeout. The 1st thing I will work on is making routingLookupHttpClient and routingLookupHttpSender inner classes to simplify my namespace and number of beans I have to create for each new service.
conclusion
I think using the best of breed WebServiceTemplate as well as XXX allowed me to easily create webservice read timeouts and made testing very easy.

