Passing Runtime Information

The previous sinks were IClientChannelSinks and IServerChannelSinks. This means that they work on the resulting stream after the formatter has serialized the IMessage object. IMessageSinks, in contrast, can work directly on the message's contents before they are formatted. This means that any changes you make to the IMessage's contents will be serialized and therefore reflected in the resulting stream.

Caution 

Even though you might be tempted to change the IMessage object's content in an IClientChannelSink, be aware that this change is not propagated to the server, because the serialized stream has already been generated from the underlying IMessage!

Because of this distinction, client-side IMessageSinks can be used to pass runtime information from the client to the server. In the following example, I show you how to send the client-side thread's current priority to the server so that remote method calls will execute with the same priority.

To send arbitrary data from the client to the server, you can put it into the Message object's logical call context. In this way, you can transfer objects that either are serializable or extend MarshalByRefObject. For example, to pass the client-side thread's current context for every method call to the server, you can implement the following SyncProcessMessage() method:

 public IMessage SyncProcessMessage(IMessage msg) {     if (msg as IMethodCallMessage != null)     {       LogicalCallContext lcc =          (LogicalCallContext) msg.Properties["__CallContext"];       lcc.SetData("priority",Thread.CurrentThread.Priority);          return _nextMsgSink.SyncProcessMessage(msg);     }     else     {          return _nextMsgSink.SyncProcessMessage(msg);     } } 

The same has to be done for AsyncProcessMessage() as well:

 public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) {    if (msg as IMethodCallMessage != null)    {       LogicalCallContext lcc =          (LogicalCallContext) msg.Properties["__CallContext"];       lcc.SetData("priority",Thread.CurrentThread.Priority);       return _nextMsgSink.AsyncProcessMessage(msg,replySink);    }    else    {       return _nextMsgSink.AsyncProcessMessage(msg,replySink);    } } 

On the server side, you have to implement an IServerChannelSink to take the call context from the IMessage object and set Thread.CurrentThread.Priority to the contained value:

 public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,    IMessage requestMsg,    ITransportHeaders requestHeaders,    Stream requestStream,    out IMessage responseMsg,    out ITransportHeaders responseHeaders,    out Stream responseStream) {    LogicalCallContext lcc =       (LogicalCallContext) requestMsg.Properties["__CallContext"];    // storing the current priority    ThreadPriority oldprio = Thread.CurrentThread.Priority;    // check if the logical call context contains "priority"    if (lcc != null && lcc.GetData("priority") != null)    {       // fetch the priorty from the call context       ThreadPriority priority =          (ThreadPriority) lcc.GetData("priority");       Console.WriteLine(" -> Pre-execution priority change {0} to {1}",          oldprio.ToString(),priority.ToString());       // set the priority       Thread.CurrentThread.Priority = priority;    }    // push on the stack and pass the call to the next sink    // the old priority will be used as "state" for the response    sinkStack.Push(this,oldprio);    ServerProcessing spres = _next.ProcessMessage (sinkStack,       requestMsg, requestHeaders, requestStream,       out responseMsg,out responseHeaders,out responseStream);    //restore priority if call is not asynchronous    if (spres != ServerProcessing.Async)    {       if (lcc != null && lcc.GetData("priority") != null)       {          Console.WriteLine(" -> Post-execution change back to {0}",oldprio);          Thread.CurrentThread.Priority = oldprio;       }    }    return spres; } 

The sink provider for the server-side sink is quite straightforward. It looks more or less the same as those for the previous IServerChannelSinks.

On the client side, some minor inconveniences stem from this approach. Remember that you implemented an IMessageSink and not an IClientChannelSink in this case. Looking for an IMessageSinkProvider will not give you any results, so you'll have to implement an IClientChannelSink provider in this case as well—even though the sink is in reality an IMessageSink. The problem with this can be seen when looking at the following part of the IClientChannelSinkProvider interface:

 IClientChannelSink CreateSink(IChannelSender channel,       string url,       object remoteChannelData); 

This indicates CreateSink() has to return an IClientChannelSink in any case, even if your sink only needs to implement IMessageSink. You now have to extend your IMessageSink to implement IClientChannelSink as well. You also have to use caution because IClientChannelSink defines more methods that have to be implemented. Those methods are called when the sink is used as a channel sink (that is, after the formatter) and not as a message sink. You might not want to allow your users to position the sink after the formatter (because it wouldn't work there because it's changing the IMessage object's content), so you want to throw exceptions in those methods.

The complete client-side PriorityEmitterSink, which throws those exceptions when used in the wrong sequence, is shown in Listing 9-12.

Listing 9-12: The Complete PriorityEmitterSink

start example
 using System; using System.Collections; using System.IO; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Messaging; using System.Threading; namespace PrioritySinks {  public class PriorityEmitterSink : BaseChannelObjectWithProperties,                                         IClientChannelSink, IMessageSink   {    private IMessageSink _nextMsgSink;    public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)   {        // only for method calls        if (msg as IMethodCallMessage != null)        {           LogicalCallContext lcc =              (LogicalCallContext) msg.Properties["__CallContext"];           lcc.SetData("priority",Thread.CurrentThread.Priority);           return _nextMsgSink.AsyncProcessMessage(msg,replySink);        }        else        {           return _nextMsgSink.AsyncProcessMessage(msg,replySink);        }      }      public IMessage SyncProcessMessage(IMessage msg)      {         // only for method calls         if (msg as IMethodCallMessage != null)         {            LogicalCallContext lcc =               (LogicalCallContext) msg.Properties["__CallContext"];            lcc.SetData("priority",Thread.CurrentThread.Priority);            return _nextMsgSink.SyncProcessMessage(msg);         }         else         {            return _nextMsgSink.SyncProcessMessage(msg);         }      }      public PriorityEmitterSink (object next)      {         if (next as IMessageSink != null)         {            _nextMsgSink = (IMessageSink) next;         }      }      public IMessageSink NextSink      {         get         {            return _nextMsgSink;         }      }      public IClientChannelSink NextChannelSink      {         get         {            throw new RemotingException("Wrong sequence.");         }      }      public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,         IMessage msg,         ITransportHeaders headers,         Stream stream)      {         throw new RemotingException("Wrong sequence.");      }      public void AsyncProcessResponse(         IClientResponseChannelSinkStack sinkStack,         object state,         ITransportHeaders headers,         Stream stream)      {         throw new RemotingException("Wrong sequence.");      }      public System.IO.Stream GetRequestStream(IMessage msg,         ITransportHeaders headers)      {         throw new RemotingException("Wrong sequence.");      }      public void ProcessMessage(IMessage msg,         ITransportHeaders requestHeaders,         Stream requestStream,         out ITransportHeaders responseHeaders,         out Stream responseStream)      {         throw new RemotingException("Wrong sequence.");      }   } } 
end example

The client-side PriorityEmitterSinkProvider, which is shown in Listing 9-13, is quite straightforward to implement. The only interesting method is CreateSink().

Listing 9-13: The Client-Side PriorityEmitterSinkProvider

start example
 using System; using System.Collections; using System.Runtime.Remoting.Channels; namespace PrioritySinks {    public class PriorityEmitterSinkProvider: IClientChannelSinkProvider    {       private IClientChannelSinkProvider next = null;       public PriorityEmitterSinkProvider(IDictionary properties,           ICollection providerData)       {          // not needed       }       public IClientChannelSink CreateSink(IChannelSender channel,           string url, object remoteChannelData)       {          IClientChannelSink nextsink =             next.CreateSink(channel,url,remoteChannelData);          return new PriorityEmitterSink(nextsink);       }       public IClientChannelSinkProvider Next       {          get { return next; }          set { next = value; }       }    } } 
end example

Because the server-side sink shown in Listing 9-14 is an IServerChannelSink and not an IMessageSink, as is the client-side sink, the implementation is more consistent. You don't need to implement any additional interface here.

Listing 9-14: The Server-Side PriorityChangerSink

start example
 using System; using System.Collections; using System.IO; using System.Runtime.Remoting; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Channels; using System.Threading; namespace PrioritySinks {    public class PriorityChangerSink : BaseChannelObjectWithProperties,       IServerChannelSink, IChannelSinkBase    {       private IServerChannelSink _next;       public PriorityChangerSink (IServerChannelSink next)       {          _next = next;       }       public void AsyncProcessResponse (                   IServerResponseChannelSinkStack sinkStack,                   Object state,                   IMessage msg,                   ITransportHeaders headers,                   Stream stream)       {          // restore the priority          ThreadPriority priority = (ThreadPriority) state;          Console.WriteLine(" -> Post-execution change back to {0}",priority);          Thread.CurrentThread.Priority = priority;       }       public Stream GetResponseStream (IServerResponseChannelSinkStack sinkStack,                   Object state,                   IMessage msg,                   ITransportHeaders headers )       {          return null;       }       public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,          IMessage requestMsg,          ITransportHeaders requestHeaders,          Stream requestStream,          out IMessage responseMsg,          out ITransportHeaders responseHeaders,          out Stream responseStream)       {          LogicalCallContext lcc =             (LogicalCallContext) requestMsg.Properties["__CallContext"];          // storing the current priority          ThreadPriority oldprio = Thread.CurrentThread.Priority;          // check if the logical call context contains "priority"          if (lcc != null && lcc.GetData("priority") != null)          {             // fetch the priorty from the call context             ThreadPriority priority =                (ThreadPriority) lcc.GetData("priority");             Console.WriteLine("-> Pre-execution priority change {0} to {1}",                oldprio.ToString(),priority.ToString());             // set the priority             Thread.CurrentThread.Priority = priority;          }          // push on the stack and pass the call to the next sink          // the old priority will be used as "state" for the response          sinkStack.Push(this,oldprio);          ServerProcessing spres = _next.ProcessMessage (sinkStack,             requestMsg, requestHeaders, requestStream,             out responseMsg,out responseHeaders,out responseStream);          //restore priority if call is not asynchronous          if (spres != ServerProcessing.Async)          {             if (lcc != null && lcc.GetData("priority") != null)             {                Console.WriteLine("-> Post-execution change back to {0}",                        oldprio);                Thread.CurrentThread.Priority = oldprio;             }          }          return spres;       }       public IServerChannelSink NextChannelSink       {          get {return _next;}          set {_next = value;}       }    } } 
end example

The corresponding server-side sink provider, which implements IServerChannelSinkProvider, is shown in Listing 9-15.

Listing 9-15: The Server-Side PriorityChangerSinkProvider

start example
 using System; using System.Collections; using System.Runtime.Remoting.Channels; namespace PrioritySinks {    public class PriorityChangerSinkProvider: IServerChannelSinkProvider    {       private IServerChannelSinkProvider next = null;       public PriorityChangerSinkProvider(IDictionary properties,          ICollection providerData)       {          // not needed       }       public void GetChannelData (IChannelDataStore channelData)       {          // not needed       }       public IServerChannelSink CreateSink (IChannelReceiver channel)       {          IServerChannelSink nextSink = next.CreateSink(channel);          return new PriorityChangerSink(nextSink);       }       public IServerChannelSinkProvider Next       {          get { return next; }          set { next = value; }       }    } } 
end example

To test this sink combination, use the following SAO, which returns the server-side thread's current priority:

 public class TestSAO: MarshalByRefObject {    public String getPriority()    {       return System.Threading.Thread.CurrentThread.Priority.ToString();    } } 

This SAO is called several times with different client-side thread priorities. The configuration file that is used by the server is shown here:

 <configuration>   <system.runtime.remoting>     <application>       <channels>         <channel ref="http" port="5555">         <serverProviders>           <formatter ref="soap" />                   <provider            type="PrioritySinks.PriorityChangerSinkProvider, PrioritySinks" />         </serverProviders>         </channel>       </channels>       <service>         <wellknown mode="Singleton"                 type="Server.TestSAO, Server" objectUri="TestSAO.soap"/>       </service>     </application>   </system.runtime.remoting> </configuration> 

The client-side configuration file will look like this:

 <configuration>  <system.runtime.remoting>      <application>          <channels>           <channel ref="http">             <clientProviders>               <provider                  type="PrioritySinks.PriorityEmitterSinkProvider, PrioritySinks" />               <formatter ref="soap" />            </clientProviders>         </channel>      </channels>      <client>         <wellknown type="Server.TestSAO, generated_meta"                     url="http://localhost:5555/TestSAO.soap" />      </client>    </application>   </system.runtime.remoting> </configuration> 

For the test client, you can use SoapSuds to extract the metadata. When you run the application in Listing 9-16, you'll see the output shown in Figure 9-8.

click to expand
Figure 9-8: The test client's output shows that the sinks work as expected.

Listing 9-16: The Test Client

start example
 using System; using System.Runtime.Remoting; using Server; // from generated_meta.dll using System.Threading; namespace Client {    delegate String getPrioAsync();    class Client    {       static void Main(string[] args)       {          String filename = "client.exe.config";          RemotingConfiguration.Configure(filename);          TestSAO obj = new TestSAO();          test(obj);          Thread.CurrentThread.Priority = ThreadPriority.Highest;          test(obj);          Thread.CurrentThread.Priority = ThreadPriority.Lowest;          test(obj);          Thread.CurrentThread.Priority = ThreadPriority.Normal;          test(obj);          Console.ReadLine();       }       static void test(TestSAO obj)       {          Console.WriteLine("----------------- START TEST CASE ---------------");          Console.WriteLine(" Local Priority: {0}",                                 Thread.CurrentThread.Priority.ToString());          String priority1 = obj.getPriority();          Console.WriteLine(" Remote priority: {0}",priority1.ToString());          Console.WriteLine("----------------- END TEST CASE ---------------");       }    } } 
end example




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