Making Spring’s JaxRpcPortClientInterceptor recover from Axis failures
UPDATE 10 July 2008: This issue has been resolved in Spring 2.0.3 and later by introducing a "refreshServiceAfterConnectFailure" property. Unless you are stuck with an older version of Spring, do not use the solution offered below.
The Spring framework includes a very handy class in the form of JaxRpcPortClientInterceptor as part of the Remoting package. This class allows you to treat web services just like any other dependency - configure, create and inject them in your application context XML file. This is quite clever in that you can write code that works against a Java interface and doesn't care about the underlying remoting protocol used.
The JaxRpcPortClientInterceptor class has a "lookupServiceOnStartup" property which lets you defer the WSDL fetching and initialization until the first request is made. This can help you if the server hosting the web service is not up at the time when the port interceptor is being initialized, however it doesn't help if the web service host disappears afterwards or does not become available until after a call is made. It would appear that Apache Axis (which is the actual JAX-RPC implementation used) gets confused quite easily by network failures and so it helps if you re-create the JaxRpcPortClientInterceptor whenever such a failure is detected. However, if you wanted to keep the benefits of IoC, handling these failures could result in a complicated tangle of BeanFactoryAware factories and exception handling code - not exactly what we want after we've seen just how elegant SOAP access can be with a little Spring magic. I found that in some cases I had to redeploy my application after the web service host disappeared for a short period of time - this is unacceptable in a production environment.
The following code uses Spring's own AOP framework to implement a form of failure recovery completely transparently to existing code. This demonstrates the combined power of IoC and AOP quite well - they allow us to modify behaviour without touching the existing code which was not explicitly designed for pure OOP extensibility (see the Open-Closed Principle about that).
The following XML fragment demonstrates the "pure Spring" approach to configuring web service access using Axis:
And here is the reworked example which makes use of the custom TargetSource:
Source code: