Using a Custom Proxy

In the previous parts of this chapter, you read about the possible ways you can extend the .NET Remoting Framework using additional custom message sinks. There is another option for changing the default behavior of the remoting system: custom proxy objects. Figure 9-9 shows you the default proxy configuration.

click to expand
Figure 9-9: The default combination of proxy objects

You can change this by replacing RemotingProxy with a custom proxy that inherits from RealProxy.

Note 

You'll normally miss the opportunity to use configuration files in this case. To work around this issue, you can use the RemotingHelper class, discussed in Chapter 6.

To do this, you basically have to implement a class that extends RealProxy, provides a custom constructor, and overrides the Invoke() method to pass the message on to the correct message sink.

As shown in Listing 9-20, the constructor first has to call the base object's constructor and then checks all registered channels to determine whether they accept the given URL by calling their CreateMessageSink() methods. If a channel can service the URL, it returns an IMessageSink object that is the first sink in the remoting chain. Otherwise it returns null. The constructor will throw an exception if no registered channel is able to parse the URL.

Listing 9-20: A Skeleton Custom Remoting Proxy

start example
 using System; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Proxies; using System.Runtime.Remoting.Messaging; namespace Client {    public class CustomProxy: RealProxy    {       String _url;       String _uri;       IMessageSink _sinkChain;       public CustomProxy(Type type, String url) : base(type)       {          _url = url;          // check each registered channel if it accepts the          // given URL          IChannel[] registeredChannels = ChannelServices.RegisteredChannels;          foreach (IChannel channel in registeredChannels )          {             if (channel is IChannelSender)             {                IChannelSender channelSender = (IChannelSender)channel;                // try to create the sink                _sinkChain = channelSender.CreateMessageSink(_url,                   null, out _uri);                // if the channel returned a sink chain, exit the loop                if (_sinkChain != null) break;             }          }          // no registered channel accepted the URL          if (_sinkChain == null)          {             throw new Exception("No channel has been found for " + _url);          }       }       public override IMessage Invoke(IMessage msg)       {          msg.Properties["__Uri"] = _url;          // TODO: process the request message          IMessage retMsg = _sinkChain.SyncProcessMessage(msg);          // TODO: process the return message          return retMsg;       }    } } 
end example

To employ this proxy, you have to instantiate it using the special constructor by passing the Type of the remote object and its URL. You can then call GetTransparentProxy() on the resulting CustomProxy object and cast the returned TransparentProxy to the remote object's type, as shown in Listing 9-21.

Listing 9-21: Using a Custom Proxy

start example
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Service; // from service.dll namespace Client {    class Client    {       static void Main(string[] args)       {          ChannelServices.RegisterChannel(new HttpChannel());          CustomProxy prx = new CustomProxy(typeof(Service.SomeSAO),             "http://localhost:1234/SomeSAO.soap");          SomeSAO obj = (SomeSAO) prx.GetTransparentProxy();          String res = obj.doSomething();          Console.WriteLine("Got result: {0}",res);          Console.ReadLine();       }    } } 
end example

To show you an example for a custom proxy, I implement some methods that dump the request and return messages' contents. These methods are called from the proxy's Invoke(), which will be executed whenever your client calls a method on the TransparentProxy object. This is shown in Listing 9-22.

Note 

You can also call these content dump methods in an IMessageSink!

Listing 9-22: This Custom Proxy Dumps the Request and Response Messages' Contents

start example
 using System; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Proxies; using System.Runtime.Remoting.Messaging; namespace Client {    public class CustomProxy: RealProxy    {       String _url;       String _uri;       IMessageSink _sinkChain;       public CustomProxy(Type type, String url) : base(type)       {          _url = url;          // check each registered channel if it accepts the          // given URL          IChannel[] registeredChannels = ChannelServices.RegisteredChannels;          foreach (IChannel channel in registeredChannels )          {             if (channel is IChannelSender)             {                IChannelSender channelSender = (IChannelSender)channel;                // try to create the sink                _sinkChain = channelSender.CreateMessageSink(_url,                   null, out _uri);                // if the channel returned a sink chain, exit the loop                if (_sinkChain != null) break;             }          }          // no registered channel accepted the URL          if (_sinkChain == null)          {             throw new Exception("No channel has been found for " + _url);          }       }       public override IMessage Invoke(IMessage msg)       {          msg.Properties["__Uri"] = _url;          DumpMessageContents(msg);          IMessage retMsg = _sinkChain.SyncProcessMessage(msg);          DumpMessageContents(retMsg);          return retMsg;       }       private String GetPaddedString(String str)       {          String ret = str + " ";          return ret.Substring(0,17);       }       private void DumpMessageContents(IMessage msg)       {          Console.WriteLine("========================================");          Console.WriteLine("============ Message Dump ==============");          Console.WriteLine("========================================");          Console.WriteLine("Type: {0}", msg.GetType().ToString());          Console.WriteLine("--- Properties ---");          IDictionary dict = msg.Properties;          IDictionaryEnumerator enm =                  (IDictionaryEnumerator) dict.GetEnumerator();          while (enm.MoveNext())          {             Object key = enm.Key;             String keyName = key.ToString();             Object val = enm.Value;             Console.WriteLine("{0}: {1}", GetPaddedString(keyName), val);             // check if it's an object array             Object[] objval = val as Object[];             if (objval != null)             {                DumpObjectArray(objval);             }          }          Console.WriteLine();          Console.WriteLine();       }       private void DumpObjectArray(object[] data)       {          // if empty -> return          if (data.Length == 0) return;          Console.WriteLine("\t --- Array Contents ---");          for (int i = 0; i < data.Length; i++)          {             Console.WriteLine("\t{0}: {1}", i, data[i]);          }       }    } } 
end example

The output of the preceding client when used with this proxy is shown in Figure 9-10.

click to expand
Figure 9-10: Using the custom proxy to dump the messages' contents

Because nearly all the functionality you'll want to implement in a common .NET Remoting scenario can be implemented with IMessageSinks, IClientChannelSinks, or IServerChannelSinks, I suggest you implement functionality by using these instead of custom proxies in .NET Remoting. Sinks provide the additional benefits of being capable of working with configuration files and being chainable. This chaining allows you to develop a set of very focused sinks that can then be combined to solve your application's needs.

Custom proxies are nevertheless interesting because they can also be used for local objects. In this case, you don't have to implement a special constructor, only override Invoke(). You can then pass any MarshalByRefObject to another constructor (which is provided by the parent RealProxy) during creation of the proxy. All method calls to this local object then pass the proxy as an IMessage object and can therefore be processed. You can read more on message-based processing for local applications in Chapter 11.




Advanced  .NET Remoting C# Edition
Advanced .NET Remoting (C# Edition)
ISBN: 1590590252
EAN: 2147483647
Year: 2002
Pages: 91
Authors: Ingo Rammer

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net