This is the 5th step - Simple DynamicProxy.
The plot thickens..
As mentioned before one of our main goals is to develop a proxy that can work both when the server is connected & disconnected.
We saw clues for the main idea in previous articles of this tutorial.
On "Online mode" the application will work with using regular TCP connection.
On "Offline mode" it will use MSMQ-Duplex.
But how can we identify the need for the switch between them? how can we change between them? can we make the change transparent for the proxy consumer??
The best solution I found for this is using a wonderfull design pattern called - dynamic proxy - this pattern allows us to include dynamic behavior around an object, yet neither change the object's existing code nor its interface
In a previous article of mine I've explored a use of this pattern building a
transverse application policy (AOP – Aspect Orient Programming in .NET).
This design pattern is nicely implemented in a free & open source library called "Castle DynamicProxy" written by a group called "Castle Project".
If you want to read more about the uses of this library, I recommend Krzysztof Koźmic's blog (Castle Dynamic Proxy tutorial).
Back to our little project, we'll start exploring the use of dynamic proxy relevant for the previously mentioned goal.
1st we'll look at the contract..simple as in previous samples:
[ServiceContract]
public interface ISampleContract
{
[OperationContract(IsOneWay=true)]
void Execute(Guid identifier);
}
Host stays the same, the server could not be simpler:
public class testFancyProxyService:ISampleContract
{
#region ISampleContract Members
public void Execute(Guid identifier)
{
Console.WriteLine("recieved Execute request (id {0})", identifier);
}
#endregion
}
In server's (and client's) app.config we will add two endpoints, TCP for the 'online mode' and MSMQ for the 'offline mode':
<endpoint name="Online"
address="net.tcp://localhost:8080/testFancyProxy" binding="netTcpBinding"
bindingConfiguration=""
contract="testFancyProxyContracts.ISampleContract" />
...
<endpoint name="Offline"
address="net.msmq://localhost/private/testFancyProxy"
binding="netMsmqBinding" bindingConfiguration="NoMSMQSecurity"
contract="testFancyProxyContracts.ISampleContract" />
Now lets look at real point of this article, in the client side I've written a new interface & two classes (besides the references to "Castle dynamic proxy" of course).
The interface IProxySelectorDesicion speaks for itself:
public interface IProxySelectorDesicion
{
bool IsOnline { get; }
}
The 'DynamicTargetProxyFactory' receives in it's ctor two objects, in our case the first one will represent the 'online' proxy & the second the 'offline'.
///
/// builds the transparent proxy
/// and registers the relevant interceptor
///
///
public class DynamicTargetProxyFactory
{
private readonly IProxySelectorDesicion _primaryTarget;
private readonly T _secondaryTarget;
private ProxyGenerator _generator;
public DynamicTargetProxyFactory(IProxySelectorDesicion primaryTarget,
T secondaryTarget)
{
_primaryTarget = primaryTarget;
_secondaryTarget = secondaryTarget;
_generator = new ProxyGenerator();
}
...
This class is also responssible to register the interceptor, the interceptor that will later let us "inject" our code to message chain.
...
public T GetCurrentTarget()
{
var interceptor = new DynamicTargetProxyInterceptor(_secondaryTarget);
object target = _generator.CreateInterfaceProxyWithTargetInterface(typeof(T), _primaryTarget, interceptor);
return (T)target;
}
...
The 2nd class is the acutal interceptor I've called 'DynamicTargetProxyInterceptor'. As you can see next, the interceptor checks on every intercept if primaryTarget.IsOnline (primaryTarget in our case will be the 'online mode' proxy)and if the answer is 'no' it uses 'ChangeInvocationTarget' method to switch to the secondary target (in our case the 'offline mode' proxy).
ain't that cool?! :-)
///
/// this class intercepts each call to proxy
/// and check if it needs to switch to secondary target
///
public class DynamicTargetProxyInterceptor: IInterceptor
{
private readonly T _secondaryTarget;
public DynamicTargetProxyInterceptor(T secondaryTarget)
{
_secondaryTarget = secondaryTarget;
}
public void Intercept(IInvocation invocation)
{
var primaryTarget = invocation.InvocationTarget as IProxySelectorDesicion;
if (primaryTarget.IsOnline == false)
{
ChangeToSecondaryTarget(invocation);
}
invocation.Proceed();
}
private void ChangeToSecondaryTarget(IInvocation invocation)
{
var changeProxyTarget = invocation as IChangeProxyTarget;
changeProxyTarget.ChangeInvocationTarget(_secondaryTarget);
}
}
Now lets look at the proxy, this time I've used the "Add service reference.." on Visual Studio & just expanded it using parallel partial class.
This class implements 'IProxySelectorDesicion' and registers to 'NetworkAvailabilityChanged' event to be update the network availability status.
public partial class SampleContractClient : IProxySelectorDesicion
{
private bool isOnline;
public SampleContractClient(string endpointConfigurationName, bool dummy)
: base(endpointConfigurationName)
{
bool connected = NetworkInterface.GetIsNetworkAvailable();
UpdateConnectionStatus(connected);
NetworkChange.NetworkAvailabilityChanged += OnAvaiabilityChanged;
}
#region IProxySelectorDesicion Members
public bool IsOnline
{
get { return isOnline; }
}
#endregion
private void OnAvaiabilityChanged(object sender, NetworkAvailabilityEventArgs args)
{
UpdateConnectionStatus(args.IsAvailable);
}
private void UpdateConnectionStatus(bool connected)
{
isOnline = connected;
}
}
Finally let's look at the consumer, here we can see we construct two proxies (offline/online) send them both to the 'DynamicTargetProxyFactory' and execute...
The 'DynamicTargetProxyFactory' will handle everything and change between this two proxies depending on the network availability :-)
public class testFancyProxyConsumer
{
private const string ONLINE_ENDPOINT = "Online";
private const string OFFLINE_ENDPOINT = "Offline";
private SampleContractClient onlineProxy;
private SampleContractClient offlineProxy;
private ISampleContract proxy;
private DynamicTargetProxyFactorydp;
public void Run()
{
onlineProxy = new SampleContractClient(ONLINE_ENDPOINT, true);
offlineProxy = new SampleContractClient(OFFLINE_ENDPOINT, true);
dp = new DynamicTargetProxyFactory(onlineProxy, offlineProxy);
proxy = dp.GetCurrentTarget();
proxy.Execute(Guid.NewGuid());
}
}
Testing...
To test the switch between proxies, we will do the following steps:
1. Put the server on different machine (don't forget to open the server queue & change the client config to point the server machine).
2. Add a console.writeline on the proxy's desicion in Intercept method in DynamicTargetProxyInterceptor and the 'proxy.Execute' in a loop.
3. Start the server.
4. Disconnect the client from the network.
5. Run the application in the client - we'll see the use of the offline proxy (using MSMQ in this case).
6. Connect the client to the network, call a server method, the online proxy will be chosen (regular TCP channel).
7. The server will receive all calls (thanks to MSMQ...).
The result:
On the left we can see the client in a local machine, we can see the switch between the offline & the online proxy, on the right we can see the remote server machine showing that all the messages arrived - offline & online...
This step's mission is accomplished.
Till next time...
Diego
PS: Source Download
Please help to post the missing reference .I need your source.
ReplyDelete