Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
jweber committed Oct 8, 2014
2 parents 02e8c56 + cc165fa commit 48113c2
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .semver
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
:major: 2
:minor: 0
:patch: 6
:patch: 7
:special: ''
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ For example, if sensitive information is needed to be stripped out of certain re
#### MaximumRetries(int retryCount)
Sets the maximum amount of times the the proxy will additionally attempt to call the service in the event it encounters a known retry-friendly exception or response. If retryCount is set to 0, then only one request attempt will be made.

#### RetryFailureExceptionFactory(RetryFailureExceptionFactoryDelegate)
By default, if the value configured in the `MaximumRetries` call is exceeded (i.e. a WCF call could not successfully be made after more than 1 attempt), a `WcfRetryFailedException` exception is thrown. In some cases, it makes sense to throw an exception of a different type. This method can be used to define a factory that generates the `Exception` that is thrown.

The signature of this delegate is:

public delegate Exception RetryFailureExceptionFactoryDelegate(int retryAttempts, Exception lastException, InvokeInfo invocationInfo);

#### RetryOnException\<TException\>(Predicate\<TException\> where = null)
Configures the proxy to retry calls when it encounters arbitrary exceptions. The optional `Predicate<Exception>` can be used to refine properties of the Exception that it should retry on.

Expand Down
38 changes: 38 additions & 0 deletions source/WcfClientProxyGenerator.Tests/ProxyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,24 @@ public void Proxy_ReturnsExpectedValue_WhenCallingService()
[Test]
public void Proxy_CanCallVoidMethod()
{
var resetEvent = new AutoResetEvent(false);

var mockService = new Mock<ITestService>();
mockService
.Setup(m => m.VoidMethod("good"))
.Callback<string>(input =>
{
Assert.That(input, Is.EqualTo("good"));
resetEvent.Set();
});

var serviceHost = InProcTestFactory.CreateHost<ITestService>(new TestServiceImpl(mockService));

var proxy = WcfClientProxy.Create<ITestService>(c => c.SetEndpoint(serviceHost.Binding, serviceHost.EndpointAddress));
proxy.VoidMethod("good");

if (!resetEvent.WaitOne(300))
Assert.Fail("Timeout occurred when waiting for callback");
}

[Test]
Expand Down Expand Up @@ -217,6 +223,38 @@ public void Proxy_ConfiguredWithAtLeastOnRetry_CallsServiceMultipleTimes_AndThro
mockService.Verify(m => m.VoidMethod("test"), Times.Exactly(2));
}

[Test]
public void Proxy_ConfiguredWithAtLeastOnRetry_CallsServiceMultipleTimes_AndThrowsCustomRetryFailureException()
{
var mockService = new Mock<ITestService>();
mockService
.Setup(m => m.VoidMethod("test"))
.Throws(new FaultException());

var serviceHost = InProcTestFactory.CreateHost<ITestService>(new TestServiceImpl(mockService));

var proxy = WcfClientProxy.Create<ITestService>(c =>
{
c.SetEndpoint(serviceHost.Binding, serviceHost.EndpointAddress);
c.MaximumRetries(1);
c.RetryOnException<FaultException>();
c.RetryFailureExceptionFactory((attempts, exception, info) =>
{
string message = string.Format("Failed call to {0} {1} times", info.MethodName, attempts);
return new CustomFailureException(message, exception);
});
});

Assert.That(() => proxy.VoidMethod("test"), Throws.TypeOf<CustomFailureException>());
mockService.Verify(m => m.VoidMethod("test"), Times.Exactly(2));
}

public class CustomFailureException : Exception
{
public CustomFailureException(string message, Exception innerException) : base(message, innerException)
{}
}

#region Out Parameter Support

[Test]
Expand Down
3 changes: 3 additions & 0 deletions source/WcfClientProxyGenerator/DefaultProxyConfigurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ public static void Configure<TServiceInterface>(IRetryingProxyConfigurator proxy

public static readonly Func<LinearBackoffDelayPolicy> DefaultDelayPolicyFactory
= () => new LinearBackoffDelayPolicy(TimeSpan.FromMilliseconds(500), TimeSpan.FromSeconds(10));

public static readonly RetryFailureExceptionFactoryDelegate DefaultRetryFailureExceptionFactory
= (retryCount, lastException, invokInfo) => new WcfRetryFailedException(string.Format("WCF call failed after {0} retries.", retryCount), lastException);
}
}
16 changes: 16 additions & 0 deletions source/WcfClientProxyGenerator/IRetryingProxyConfigurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

namespace WcfClientProxyGenerator
{
/// <summary>
/// Signature to use when defining a custom exception to use in place of the built in <see cref="WcfRetryFailedException"/>
/// </summary>
/// <param name="retryAttempts">The total amount of retry attempts made before ultimately failing</param>
/// <param name="lastException">The last exception (if any) encountered when retrying the WCF calls</param>
/// <param name="invocationInfo">Additional information about the WCF request</param>
public delegate Exception RetryFailureExceptionFactoryDelegate(int retryAttempts, Exception lastException, InvokeInfo invocationInfo);

/// <summary>
/// Proxy configuration options for managing retry logic
/// </summary>
Expand Down Expand Up @@ -43,5 +51,13 @@ void RetryOnException<TException>(Predicate<TException> where = null)
/// <typeparam name="TResponse"></typeparam>
/// <param name="where">Predicate defining the case to retry on</param>
void RetryOnResponse<TResponse>(Predicate<TResponse> where);

/// <summary>
/// Overrides the default use of the <see cref="WcfRetryFailedException"/> type when
/// the WCF call fails more than <see cref="MaximumRetries"/> and uses the
/// <see cref="Exception"/> type generated by this <paramref name="factory"/> in its place.
/// </summary>
/// <param name="factory">Delegate with <c>int</c>, <c>Exception</c> and <c>InvokeInfo</c> arguments</param>
void RetryFailureExceptionFactory(RetryFailureExceptionFactoryDelegate factory);
}
}
13 changes: 7 additions & 6 deletions source/WcfClientProxyGenerator/RetryingWcfActionInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public RetryingWcfActionInvoker(
{
RetryCount = retryCount;
DelayPolicyFactory = delayPolicyFactory ?? DefaultProxyConfigurator.DefaultDelayPolicyFactory;
RetryFailureExceptionFactory = DefaultProxyConfigurator.DefaultRetryFailureExceptionFactory;

_wcfActionProviderCreator = wcfActionProviderCreator;
_retryPredicates = new Dictionary<Type, object>
Expand All @@ -70,6 +71,8 @@ public RetryingWcfActionInvoker(

public Func<IDelayPolicy> DelayPolicyFactory { get; set; }

public RetryFailureExceptionFactoryDelegate RetryFailureExceptionFactory { get; set; }

/// <summary>
/// Event that is fired immediately before the service method will be called. This event
/// is called only once per request.
Expand Down Expand Up @@ -226,9 +229,8 @@ public TResponse Invoke<TResponse>(Func<TServiceInterface, TResponse> method, In
if (RetryCount == 0)
throw mostRecentException;

throw new WcfRetryFailedException(
string.Format("WCF call failed after {0} retries.", this.RetryCount),
mostRecentException);
var exception = this.RetryFailureExceptionFactory(this.RetryCount, mostRecentException, invokeInfo);
throw exception;
}
}
finally
Expand Down Expand Up @@ -312,9 +314,8 @@ public async Task<TResponse> InvokeAsync<TResponse>(Func<TServiceInterface, Task
if (RetryCount == 0)
throw mostRecentException;

throw new WcfRetryFailedException(
string.Format("WCF call failed after {0} attempts.", RetryCount),
mostRecentException);
var exception = this.RetryFailureExceptionFactory(this.RetryCount, mostRecentException, invokeInfo);
throw exception;
}
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ public void RetryOnResponse<TResponse>(Predicate<TResponse> where)
_actionInvoker.AddResponseToRetryOn(where);
}

public void RetryFailureExceptionFactory(RetryFailureExceptionFactoryDelegate factory)
{
_actionInvoker.RetryFailureExceptionFactory = factory;
}

#endregion
}
}

0 comments on commit 48113c2

Please sign in to comment.