/// <summary> /// Repetitively executes the specified action while it satisfies the current retry policy. /// </summary> /// <typeparam name="TResult">The type of result expected from the executable action.</typeparam> /// <param name="func">A delegate representing the executable action which returns the result of type R.</param> /// <returns>The result from the action.</returns> private TResult ExecuteAction <TResult>(Func <TResult> func) { IntuitRetryHelper.IsArgumentNull(func, "func"); int retryCount = 1; TimeSpan delay = TimeSpan.Zero; Exception lastError; while (true) { lastError = null; try { return(func()); } catch (RetryExceededException retryExceededException) { // The user code can throw a RetryLimitExceededException to force the exit from the retry loop. // The RetryLimitExceeded exception can have an inner exception attached to it. This is the exception // which we will have to throw up the stack so that callers can handle it. if (retryExceededException.InnerException != null) { throw retryExceededException.InnerException; } else { return(default(TResult)); } } catch (Exception ex) { lastError = ex; if (!IsTransient(lastError) || ((this.ExtendedRetryException != null) && this.ExtendedRetryException.IsRetryException(ex))) { throw; } if (!this.shouldRetry(retryCount++, lastError, out delay)) { WebException webException = ex as WebException; string errorString = string.Empty; if (webException != null) { // If not null then check the response property of the webException object. if (webException.Response != null) { // There is a response from the Ids server. Cast it to HttpWebResponse. HttpWebResponse errorResponse = (HttpWebResponse)webException.Response; // Get the status code description of the error response. string statusCodeDescription = errorResponse.StatusCode.ToString(); // Get the status code of the error response. int statusCode = (int)errorResponse.StatusCode; ICompressor responseCompressor = CoreHelper.GetCompressor(this.context, false); if (!string.IsNullOrWhiteSpace(errorResponse.ContentEncoding) && responseCompressor != null) { using (var responseStream = errorResponse.GetResponseStream()) //Check for decompressing { using (var decompressedStream = responseCompressor.Decompress(responseStream)) { // Get the response stream. StreamReader reader = new StreamReader(decompressedStream); //StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } } else { using (Stream responseStream = errorResponse.GetResponseStream()) { // Get the response stream. StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } string response_intuit_tid_header = ""; //get intuit_tid header for (int i = 0; i < errorResponse.Headers.Count; ++i) { if (errorResponse.Headers.Keys[i] == "intuit_tid") { response_intuit_tid_header = errorResponse.Headers[i]; } } // Log the error string to disk. CoreHelper.GetRequestLogging(this.context).LogPlatformRequests(" Response Intuit_Tid header: " + response_intuit_tid_header + " Response Payload: " + errorString, false); } } Core.Rest.FaultHandler fault = new Core.Rest.FaultHandler(this.context); IdsException idsException = fault.ParseErrorResponseAndPrepareException(errorString); if (idsException != null) { throw new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, idsException); } else if (webException != null) { throw new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, webException); } throw new RetryExceededException(ex.Message, ex); } } // Perform an extra check in the delay interval. Should prevent from accidentally ending up with the value of -1 that will block a thread indefinitely. // In addition, any other negative numbers will cause an ArgumentOutOfRangeException fault that will be thrown by Thread.Sleep. if (delay.TotalMilliseconds < 0) { delay = TimeSpan.Zero; } this.OnRetrying(retryCount - 1, lastError, delay); if (retryCount > 2 && delay > TimeSpan.Zero) { Thread.Sleep(delay); } } }
public void ExecuteAction <TResult>(Action <AsyncCallback> beginAction, Func <IAsyncResult, TResult> endAction, Action <TResult> successHandler, Action <Exception> faultHandler) { IntuitRetryHelper.IsArgumentNull(beginAction, "beginAction"); IntuitRetryHelper.IsArgumentNull(endAction, "endAction"); IntuitRetryHelper.IsArgumentNull(successHandler, "successHandler"); IntuitRetryHelper.IsArgumentNull(faultHandler, "faultHandler"); int retryCount = 0; AsyncCallback endInvoke = null; Func <Action, bool> executeWithRetry = null; // Configure a custom callback delegate that invokes the end operation and the success handler if the operation succeedes endInvoke = ar => { var result = default(TResult); if (executeWithRetry(() => result = endAction(ar))) { successHandler(result); } }; // Utility delegate to invoke an action and implement the core retry logic // If the action succeeds (i.e. does not throw an exception) it returns true. // If the action throws, it analizes it for retries. If a retry is required, it restarts the async operation; otherwise, it invokes the fault handler. executeWithRetry = a => { try { // Invoke the callback delegate which can throw an exception if the main async operation has completed with a fault. a(); return(true); } catch (Exception ex) { // Capture the original exception for analysis. var lastError = ex; // Handling of RetryLimitExceededException needs to be done separately. This exception type indicates the application's intent to exit from the retry loop. if (lastError is RetryExceededException) { if (lastError.InnerException != null) { faultHandler(lastError.InnerException); return(false); } else { faultHandler(lastError); return(false); } } else { var delay = TimeSpan.Zero; // Check if we should continue retrying on this exception. If not, invoke the fault handler so that user code can take control. if (!IsTransient(lastError) || ((this.ExtendedRetryException != null) && this.ExtendedRetryException.IsRetryException(ex))) { faultHandler(lastError); return(false); } else { if (delay.TotalMilliseconds < 0) { delay = TimeSpan.Zero; } retryCount = retryCount + 1; if (!this.shouldRetry(retryCount, lastError, out delay)) { WebException webException = ex as WebException; string errorString = string.Empty; if (webException != null) { // If not null then check the response property of the webException object. if (webException.Response != null) { // There is a response from the Ids server. Cast it to HttpWebResponse. HttpWebResponse errorResponse = (HttpWebResponse)webException.Response; // Get the status code description of the error response. string statusCodeDescription = errorResponse.StatusCode.ToString(); // Get the status code of the error response. int statusCode = (int)errorResponse.StatusCode; ICompressor responseCompressor = CoreHelper.GetCompressor(this.context, false); if (!string.IsNullOrWhiteSpace(errorResponse.ContentEncoding) && responseCompressor != null) { using (var responseStream = errorResponse.GetResponseStream()) //Check for decompressing { using (var decompressedStream = responseCompressor.Decompress(responseStream)) { // Get the response stream. StreamReader reader = new StreamReader(decompressedStream); //StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } } else { using (Stream responseStream = errorResponse.GetResponseStream()) { // Get the response stream. StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } string response_intuit_tid_header = ""; //get intuit_tid header for (int i = 0; i < errorResponse.Headers.Count; ++i) { if (errorResponse.Headers.Keys[i] == "intuit_tid") { response_intuit_tid_header = errorResponse.Headers[i]; } } // Log the error string to disk. CoreHelper.GetRequestLogging(this.context).LogPlatformRequests(" Response Intuit_Tid header: " + response_intuit_tid_header + ", Response Payload: " + errorString, false); } } Core.Rest.FaultHandler fault = new Core.Rest.FaultHandler(this.context); IdsException idsException = fault.ParseErrorResponseAndPrepareException(errorString); if (idsException != null) { faultHandler(new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, idsException)); return(false); } else if (webException != null) { faultHandler(new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, webException)); return(false); } faultHandler(new RetryExceededException(ex.Message, ex)); return(false); } // Notify the respective subscribers about this exception. this.OnRetrying(retryCount, lastError, delay); // Sleep for the defined interval before repetitively executing the main async operation. if (retryCount > 2 && delay > TimeSpan.Zero) { Thread.Sleep(delay); } executeWithRetry(() => beginAction(endInvoke)); } } return(false); } }; // Invoke the the main async operation for the first time which should return control to the caller immediately. executeWithRetry(() => beginAction(endInvoke)); }