Добавить в цитаты Настройки чтения

Страница 216 из 372

11. Определение класса News во многом похоже на определение класса Tах. Достаточно только отметить, что этот класс является последним в цепочке рассылки уведомлений, что приводит к упрощению кода.

12. Класс AccountApp содержит функцию Main и определяет консольное серверное приложение. Этот класс не претерпел никаких изменений по сравнению с соответствующим классом, описанным в предыдущей главе.

Атрибут трассировки вызовов

Прежде чем привести код этого атрибута, вернемся к понятию контекста. Контекст формируется только тогда, когда это необходимо, т. е. тогда, когда появляется первый объект, который будет жить в этом контексте.

Необходимость создания нового контекста определяется в результате сопоставления требований к сервисам со стороны нового объекта, и наличных сервисов, доступных в старом контексте (в контексте, из которого был сделан запрос на формирование нового объекта). Если старый контекст удовлетворяет требованиям нового объекта, то этот новый объект размещается в старом контексте и получает возможность использовать связанные с ним сервисы. В противном случае создается новый контекст, с которым связывается некоторый набор сервисов, запрашиваемых новым объектом.

Связь сервиса с контекстом осуществляется путем задания соответствующего свойства контекста для данного контекста. Каждое свойство контекста имеет имя и содержит ссылку на некоторый объект, реализующий интерфейс IContextProperty.

Любой объект в данном контексте может по имени свойства получить доступ к соответствующему объекту-свойству и явно пользоваться всеми его возможностями (например, вызывать его методы). Такой способ явного использования контекста не очень интересен, т. к. он предполагает тесную связь между кодом компонента и кодом аспекта (сервиса, доступного через свойство контекста). При использовании чисто декларативного способа связывания компонента и аспекта необходимо обеспечить неявное связывание компонента с сервисом, когда в коде компонента нет никакого упоминания этого сервиса. Этого можно достигнуть за счет использования понятия перехвата.

Как уже упоминалось ранее, в случае пересечения вызовом границы контекста (кроме контекста по умолчанию), прокси на вызывающей стороне преобразует вызов в сообщение (объект, реализующий интерфейс IMessage), которое пройдя через цепочку перехватчиков (объектов, реализующих интерфейс IMessageSink), в вызываемом контексте вновь преобразуется в вызов, который исполняется соответствующим объектом. Результат отправляется вызывающей стороне через ту же цепочку перехватчиков.

Каждое свойство контекста может встроить в эту цепочку перехватчиков собственный перехватчик, что и создает возможность неявного вызова нужного сервиса как до, так и после каждого вызова соответствующего объекта. Для этого объект-свойство, приписаваемый контексту, должен реализовать какие-либо из трех интерфейсов:

ICcontributeObjectSink, IContributeServerContextSink, IContributeClientContextSink. Выбор одного из этих интерфейсов определяет ту цепочку перехватчиков, в конец которой будет добавлен новый перехватчик. На самом деле в контексте может существовать несколько цепочек перехватчиков:

• Одна цепочка перехватчиков, перехватывающих все вызовы поступающие ко всем объектам, живущим в данном контексте. Для встраивания перехватчика в эту цепочку объект-свойство контекста должен реализовать интерфейс IContributeServerContextSink.

• По одной цепочке к каждому объекту, живущему в контексте. В эту цепочку вызов попадает пройдя по цепочке общей для всего контекста. Для встраивания перехватчика в эту специфичную для объекта цепочку объект-свойство должен реализовать интерфейс IContributeObjectSink.

• Одна цепочка для всех вызовов, которые объекты контекста делают за пределы данного контекста. Для встраивания перехватчика в эту цепочку объект-свойство должен реализовать интерфейс IContributeClientContextSink.

После этих вводных замечаний о механизме работы атрибута и контекста перейдем к коду атрибута MyCallTraceAttribute и к комментариям к этому коду.

using System;

using System.10;

using System.Threading;

using System.Runtime.Remoting.Messaging;

using System.Runtime.Remoting.Contexts;

using System.Runtime.Remoting.Activation;

using System.Runtime.CompilerServices;

namespace SPbU.AOP_NET{

[AttributeUsage(AttributeTargets.Class)]

public class MyCaiiTraceAttribute: ContextAttribute,

       IContributeServerContextSink {

        private const String PROPERTY_NAME = "MyCallTrace";

        private String _logFileName = null;

        public MyCallTraceAttribute(String logFileName):

           base(PROPERTY_NAME) {

           if (logFileName == null) {

                throw new ArgumentNullException("logFileName");

           }

           _logFileName = logFileName;

}

public override bool IsContextOK(Context ctx,

         IConstructionCallMessage msg) {

         if (ctx == null)

               throw new ArgumentNullException("ctx");

         if (msg == null)

                throw new ArgumentNullException("msg");

         MyCallTraceAttribute property =

               (MyCallTraceAttribute)ctx.GetProperty(PROPERTY_NAME)





        if ((property!= null) &&

             (property._logFileName == _logFileName))

              return true;

        else

              return false;

}

public override void GetPropertiesForNewContext {

       IConstructionCallMessage ctorMsg) {

       ctorMsg.ContextProperties.Add((IContextProperty) this);

}

public virtual IMessageSink GetServerContextSink {

       IMessageSink nextSink) {

        MyCallTraceServerContextSink propertySink =

            new MyCallTraceServerContextSink(this, nextSink);

         return (IMessageSink)propertySink;

}

 [Methodlmpl(MethodImplOptions.Synchronized)]

 internal void LogMessage(String msg){

         StreamWriter logFile = null;

         while (logFile == null) {

                 logFile = File.AppendText(_logFileName);

         }

         logFile.WriteLine(msg);

         logFile.Close();

    }

}

internal class MyCallTraceServerContextSink: IMessageSink {

         internal IMessageSink _nextSink;

         internal MyCallTraceAttribute _property;

         internal IMessage _replyMsg;

         internal MyCallTraceServerContextSink {

              MyCaiiTraceAttribute property, IMessageSink nextSink) {

                _property = property;

                _nextSink = nextSink;

                _replyMsg = null;

     {

     public virtual IMessage SyncProcessMessage(IMessage reqMsg) {

            if (reqMsg is IMethodMessage) {

                 IMethodMessage call = reqMsg as IMethodMessage;

                lock(_property){

                       _property.LogMessage("===" + call.TypeName);