Creating a Context

When you create a class that is derived from ContextBoundObject, nothing special happens yet: by default all objects are still created in the same context. You can, however, decorate this class with an attribute that inherits from ContextAttribute and overrides the following two methods:

 public bool IsContextOK(Context ctx, IConstructionCallMessage ctor) public void GetPropertiesForNewContext(IConstructionCallMessage ctor) 

When doing this, the first method is called whenever someone is creating a new instance of the target class (for example, the previous Organization class). If it returns true, nothing happens, and the object is created in the same context as the client. There won't be the chance to intercept a call from the client to this instance by using a message sink.

If the method returns false, on the other hand, a new "virtual" remoting boundary—the context—is created. In this case the framework will subsequently call GetPropertiesForNewContext() to allow you to add the IContextProperty objects that you want to use with this context.

The implementation of a complete attribute that will later be used to create a sink to intercept calls to this object is shown in Listing 11-2.

Listing 11-2: A ContextAttribute That Allows You to Intercept Calls

start example
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Messaging; namespace ContextBound {    [AttributeUsage(AttributeTargets.Class)]    public class CheckableAttribute: ContextAttribute    {       public CheckableAttribute(): base (“MyInterception”) { }       public override bool IsContextOK(Context ctx,          IConstructionCallMessage ctor)       {          // if this is already an intercepting context, it’s ok for us          return ctx.GetProperty(“Interception”) != null;       }       public override void GetPropertiesForNewContext(          IConstructionCallMessage ctor)       {          // add the context property which will later create a sink          ctor.ContextProperties.Add(new CheckableContextProperty());       }    } } 
end example

An IContextProperty on its own doesn't provide you with a lot of functionality, as you can see in Listing 11-3.

Listing 11-3: The IContextProperty Interface

start example
 public interface IContextProperty {     string Name { get; }     void Freeze(Context newContext);     bool IsNewContextOK(Context newCtx); } 
end example

Freeze() is called when the context itself is frozen. This indicates that no change of context properties is allowed afterwards. IsNewContextOk() is called after all context attributes have added their context properties to allow your property to check for dependencies. If IContextProperty A can only be used together with IContextProperty B, it can check in this method if both properties are available for the newly created context. If this method returns false, an exception will be thrown.

Name simply has to return the context property's name that will be used to retrieve it by calling Context.GetProperty(“<name>”). To be able to create a sink to intercept calls to this object, this class will have to implement one of the following interfaces: IContributeObjectSink, IContributeEnvoySink, IContributeClientContextSink, or IContributeServerContextSink. In the examples to follow, I use IContributeObjectSink, which is shown in Listing 11-4.

Listing 11-4: The IContributeObjectSink Interface

start example
 public interface IContributeObjectSink {     IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink); } 
end example

To create a new instance of CheckerSink, you can implement the IContextProperty as shown in Listing 11-5.

Listing 11-5: The CheckableContextProperty

start example
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Messaging; namespace ContextBound {    public class CheckableContextProperty: IContextProperty,       IContributeObjectSink    {       public bool IsNewContextOK(Context newCtx)       {          return true;       }       public void Freeze(Context newContext)       {          // nothing to do       }       public string Name       {          get          {             return “Interception”;          }       }       public IMessageSink GetObjectSink(MarshalByRefObject obj,             IMessageSink nextSink)       {          return new CheckerSink(nextSink);       }    } } 
end example

CheckerSink itself is a common IMessageSink implementation. Its first iteration is shown in Listing 11-6.

Listing 11-6: The CheckerSink's First Iteration

start example
 using System; using System.Reflection; using System.Runtime.Remoting; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Messaging; namespace ContextBound {    public class CheckerSink: IMessageSink    {       IMessageSink _nextSink;       public CheckerSink(IMessageSink nextSink)       {          _nextSink = nextSink;       }       public IMessage SyncProcessMessage(IMessage msg)       {          Console.WriteLine(“CheckerSink is intercepting a call”);          return _nextSink.SyncProcessMessage(msg);       }       public IMessageCtrl AsyncProcessMessage(IMessage msg,          IMessageSink replySink)       {          Console.WriteLine(“CheckerSink is intercepting an async call”);          return _nextSink.AsyncProcessMessage(msg,replySink);       }       publicIMessageSink NextSink       {          get          {             return _nextSink.          }       }    } } 
end example

To enable this way of intercepting the Organization class shown at the beginning of this chapter, you have to mark it with [Checkable] and have it inherit from ContextBoundObject to create the context property.

The Organization class, which is shown in Listing 11-7, does not yet employ the use of custom attributes for checking the maximum amount of a single donation or the maximum length of the organization's name. It just demonstrates the basic principle of interception.

Listing 11-7: The Organization Now Is a ContextBoundObject

start example
 using System; namespace ContextBound {    [Checkable]    public class Organization: ContextBoundObject    {       String _name;       double _totalDonation;       public String Name       {          set          {             _name = value;          }          get          {             return _name;          }       }       public void Donate(double amount)       {          Organization x = new Organization();          x.Name = “Hello World”;          _totalDonation = _totalDonation + amount;       }    } } 
end example

A simple client for this class is shown in Listing 11-8.

Listing 11-8: This Client Is Using the ContextBoundObject

start example
 using System; using System.Runtime.Remoting.Contexts; namespace ContextBound {    public class TestClient    {       public static void Main(String[] args) {          Organization org = new Organization();          Console.WriteLine(“Will set the name”);          org.Name = “Happy Hackers”;          Console.WriteLine(“Will donate”);          org.Donate(103);          Console.WriteLine(“Finished, press <return> to quit.”);          Console.ReadLine();       }    } } 
end example

When this application is started, you will see the output shown in Figure 11-1.

click to expand
Figure 11-1: The application's output when using the ContextBoundObject

As you can see here, the CheckerSink intercepts the setting of the property Name and the call to Donate(), although it doesn't yet do anything to check the constraints I mentioned earlier.

The first step to enabling the sink to do something useful is to create a custom attribute that will later be used to designate a parameter's maximum length and maximum value. This attribute, which can be used for parameters and methods, stores the properties MaxLength, MaxValue, and NonNull as shown in Listing 11-9. Its DoCheck() method will later be called by the sink to check a given value against the attribute's definition.

Listing 11-9: The CheckAttribute

start example
 using System; namespace ContextBound {    [AttributeUsage (AttributeTargets.Parameter | AttributeTargets.Method)]    public class CheckAttribute: Attribute    {       private int _maxLength;       private int _maxValue;       private bool _nonNull;       public int MaxLength {          get {             return _maxLength;          }          set {             _maxLength = value;          }       }       public int MaxValue       {          get          {             return _maxValue;          }          set          {             _maxValue = value;          }       }       public bool NonNull       {          get          {             return _nonNull;          }       set       {          _nonNull = value;       }    }    public void DoCheck (Object val)       {         // check for NonNull         if (_nonNull && val == null)         {            throw new Exception(“This value must not be null”);         }         // check for MaxLength         if (_maxLength > 0 && val.ToString().Length > _maxLength)         {            throw new Exception(“This value must not be longer than “ +                      _maxLength + “ characters”);         }         // check for MaxValue         if (_maxValue > 0)         {            if ((double) val > _maxValue)            {               throw new Exception(“This value must not be higher than “ +                     _maxValue );            }         }      }   } } 
end example

To make use of this attribute in the organization class, you have to mark the parameter to Donate() and the set method for the Name property as shown here:

 public String Name {    [Check(NonNull=true,MaxLength=30)]    set    {       _name = value;    }    get    {       return _name;    } } public void Donate([Check(NonNull=true,MaxValue=100)] double amount) {    _totalDonation = _totalDonation + amount; } 




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