Potty Little Details

Just another WordPress.com weblog

Context Bound Object- Part II

leave a comment »

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 below:

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());
      }
   }
}

An IContextProperty on its own doesn’t provide you with a lot of functionality

public interface IContextProperty
{
    string Name { get; }

    void Freeze(Context newContext);
    bool IsNewContextOK(Context newCtx);
}


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

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.To create a new instance of CheckerSink, you can implement the IContextProperty as shown here
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);
      }

   }
}
CheckerSink itself is a common IMessageSink implementation. Its first iteration is shown below
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.
         }
      }
   }
}
To enable this way of intercepting the Organization class shown at first part of this series you have to mark it with [Checkable] and have it inherit from ContextBoundObject to create the context property.
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;
      }

   }
}

A simple client for this class is shown below:

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();
      }
   }
}

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 . Its DoCheck() method will later be called by the sink to check a given value against the attribute’s definition.

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 );
           }

        }
     }
  }
}

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; }
Advertisements

Written by oneil

September 9, 2008 at 3:02 pm

Posted in C#

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: