I learnt today that I can create a proxy that intercepts calls to an object, do anything with the call and forward the call to the appropriate object. The same is true for the results. The whole thing is implemented by using a RealProxy implementation. I will demonstrate the functionality with .NET Remoting.
The common interface
namespace Common
{
public interface IServer
{
int Add(int a, int b);
}
}
The server implementation
using Common;
namespace Server
{
class ServerImpl: MarshalByRefObject, IServer
{
#region IServer Members
public int Add(int a, int b)
{
return a + b;
}
#endregion
public override object InitializeLifetimeService()
{
return null;
}
}
}
The server loop
static void Main()
{
// Windows Forms settings
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Remoting loop
RemotingServices.Marshal(new SzerverImplementacio(), "Server");
ChannelServices.RegisterChannel(new TcpChannel(8080), false);
// Windows Froms Window
Application.Run(new Form1());
}
The logging proxy
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels;
using System.Collections;
namespace Kliens
{
public class LogProxy : RealProxy
{
String uri;
MarshalByRefObject obj;
IMessageSink _sinkChain;
public LogProxy(Type type, string uri) : base(type)
{
this.uri = uri;
IChannel[] registeredChannels = ChannelServices.RegisteredChannels;
foreach (IChannel chnl in registeredChannels)
{
if (chnl is IChannelSender)
{
IChannelSender chnlSender = (IChannelSender)chnl;
_sinkChain = chnlSender.CreateMessageSink(uri, null, out uri);
if (_sinkChain != null)
break;
}
}
if (_sinkChain == null)
{
throw new Exception("No channel has been found for " + uri);
}
}
public override IMessage Invoke(IMessage msg)
{
Console.WriteLine("LogProxy.Invoke Start");
Console.WriteLine("");
// Set message URI
msg.Properties["__Uri"] = uri;
// List call properties
Console.WriteLine("Call properties:");
foreach (var key in msg.Properties.Keys)
{
Console.WriteLine("{0}: {1}", key, msg.Properties[key]);
if (key.ToString() == "__Args")
{
Console.WriteLine("Arguments:");
object[] args = (object[])msg.Properties[key];
foreach (var arg in args)
{
Console.WriteLine("arg: {0}", arg);
}
}
}
// Process the request mesage
IMessage retMsg = _sinkChain.SyncProcessMessage(msg);
// List return properties
Console.WriteLine("Return properties:");
foreach (var key in retMsg.Properties.Keys)
{
Console.WriteLine("{0}: {1}", key, retMsg.Properties[key]);
}
// Process the return message
return retMsg;
}
}
}
Call the server from the client
// Without logging proxy:
// IServer server = (IServer)Activator.GetObject(typeof(IServer), "tcp://localhost:8080/Server");
TcpChannel chnl = new TcpChannel();
ChannelServices.RegisterChannel(chnl, false);
LogProxy prx = new LogProxy(typeof(IServer), "Tcp://localhost:8080/Server");
IServer server = (IServer)prx.GetTransparentProxy();
MessageBox.Show(server.Add(2, 3).ToString());
Result
All the call end response properties will be listed in the Output window (or in the Console). The call will happen as there were no proxy class taking part in the call.
Apart Remoting this is a good solution for generating any kind of proxies:
- Virtual proxies
- Security proxies
- …
RealProxy is a good extension point for an application.