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.
Recent Comments