blog community

Welcome to blog community Sign in | Join | Help
in Search

Marcel de Vries, MVP Team System

.NET Technologies, Architecture and Web Development

How to implement a generic retry mechanism for a web service proxy?

For a customer I work for I had the need to implement a web service proxy that can do an automatic retry for me when I call a web service. I have a system that has web services in the local environment, but in the case the web service is not responding I have to retry the call on another server at the other end of the country cross a slow link. I did not want to implement the logic of switching the server in all subsequent client invocations so there my quest started for a generic implementation. When investigating the proxy generated by Visual Studio I discovered that the generated class unfortunately does not provide virtual methods for the method invocations. This means I could not work my way around this using a simple inheritance solution. (Implement my own invoke and do the retry there in case of an exception)

 

But fortunately I found that the generated proxy class is derived from SoapHttpClientProtocol and that class on his turn inherits from MarshalByRefObject. In the past I have done some interception on MarshalByRefObjects and that is exactly the way I could implement this as well.

 

I created a AutoRetryProxy that derives from RealProxy. The real proxy has a method called Invoke that will pass on all methods calls. This invoke method is the place where I could do my implementation of the retry. See the code sample below.

 

public class AutoRetryProxy :RealProxy

{

  private const string normalEndpoint = "http://localhost/webservice1/service1.asmx";

  private const string alternativeEndpoint = "http://localhost/webservice1/service2.asmx";

 

  HttpWebClientProtocol target;

 

  public AutoRetryProxy (HttpWebClientProtocol targetObj) : base(targetObj.GetType())

  {

   target=targetObj;

  }

 

  public override IMessage Invoke(IMessage message)

  {

   // just let the remoting infrastructure handle the call

   IMethodReturnMessage returnMessage =

                    RemotingServices.ExecuteMessage(target, (IMethodCallMessage) message);

 

   // check if we got an exception that we need to handle. These are WebExceptions

   // that indicate connectivity problems

   if((returnMessage.Exception != null) &&

                                  (returnMessage.Exception is System.Net.WebException))

   {

     System.Net.WebException ex = (System.Net.WebException )returnMessage.Exception;

     if((ex.Status == WebExceptionStatus.ConnectFailure) ||

                                               (ex.Status == WebExceptionStatus.Timeout))

       {

         // Do some nifty alternative service discovery here :-)

         target.Url = alternativeEndpoint;

        // now retry the call on the alternative server

         returnMessage =

                    RemotingServices.ExecuteMessage(target, (IMethodCallMessage) message);               

       }

     }

     return returnMessage;

   }

 

   public static object CreateProxy(Type generatedWebserviceProxyType)

   {

     if(generatedWebserviceProxyType.IsSubclassOf(typeof(SoapHttpClientProtocol)))

     {

       SoapHttpClientProtocol webserviceProxy =

         (SoapHttpClientProtocol)Activator.CreateInstance(generatedWebserviceProxyType);

       // use the default endpoint

       webserviceProxy.Url = normalEndpoint;

       return new AutoRetryProxy(webserviceProxy).GetTransparentProxy();

     }

     else

       throw new ArgumentException("The type must be a derived from SoapHttpClientProtocol");

   }

  }

 

At the client side I only need to create the instance of the web service proxy generated by Visual Studio using the static Method I created on the AutoRetryProxy proxy.  You can see the client only needs to do a different create then the new operator, but this saves a lot of other code so that works fine for me. See the code below on how the client works with the proxy.

 

private void button1_Click(object sender, System.EventArgs e)

{

  webservice.Timeout = 5000;

  label1.Text =  webservice.HelloWorld();     

}

 

private void Form1_Load(object sender, System.EventArgs e)

{

  webservice = (Service1)AutoRetryWSProxy.AutoRetryProxy.CreateProxy(typeof(Service1));

}

 

 

Published Tuesday, June 21, 2005 9:00 AM by marcelv

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server, by Telligent Systems