Tuesday, October 16, 2007

Auto-Repeat of Server Calls

Problem: When doing client/server-programming, the client calls a function on the server -- EJB, RMI, Web-Services, etc., in a language such as Java. Sometimes, the connection to the server gets lost, as the server is restarted, redeployed, network was down and up again, etc. Then, the client's call to the server fails; after reconnecting, it should be possible to repeat the call and get the wanted result. Possibly, the reconnection will take some time, if the network has a longer "outing". How can we ensure that the call is done in any case, even if there are transient network/deployment problems? Solution 1: The client's Business Delegate (you have one, haven't you?) calls a "ping()" or another such method before calling the real business method on the server. If the connection is broken, catch the exception, try to reconnect, until such a ping goes through. Disadvantages:

  • doubles the number of c/s calls
  • if the ping succeeds, but the real call fails due to problems in this nano second, you're out of luck
Solution 2: Wrap the call to the server inside a runnable, and repeat the call until it succeeds. Requires a language like Java, that supports runnable concepts. Let's see ... some code, untested, out of thin air.
 protected IServer mServer;

public ResultObject businessMethod(final int intParam, final String stringParam)
    throws BusinessException {
 class BusinessMethodRunnable implements Runnable() {
   private ResultObject result;
   public void run() {
     // do the server call itself, and store the result.
     result = mServer.businessMethod(intParam, stringParam);
   }
   public ResultObject getResultObject() {
     return resultObject;
   }
 }

 BusinessMethodRunnable runnable = new BusinessMethodRunnable();
 // wraps the call, to ensure successful completion
 callMethod(runnable);
 return runnable.getResultObject();
}

protected void callMethod(Runnable runnable)
   throws BusinessException {
 // repeat, until the loop is left with return after a successful call
 while (true) {
   try {
     if (mServer==null)
       // should block until the connection has been established
       connectToServer();
     // do the real call here.
     runnable.run();
     return;
   }
   // a business exception needs to be passed to the calling code
   catch (BusinessException e) {
     throw e;
   }
   // a runtime exception also needs to be passed to the calling code ...
   catch (RuntimeExceptione ) {
     throw e;
     // alternatively:
     // throw new BusinessException("RuntimeException caught", e);
   }
   catch (Exception e) {
     logException(e);
     // connection down, no resources to be freed(?)
     mServer = null;
   }
 }
}

No comments: