Интернет-журнал "Домашняя лаборатория", 2007 №6 - Усманов
Как уже упоминалось ранее, в случае пересечения вызовом границы контекста (кроме контекста по умолчанию), прокси на вызывающей стороне преобразует вызов в сообщение (объект, реализующий интерфейс 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);
_property.LogMessage("n" + call.MethodName +
" nt <<<IN>>> parameters: (");
for (int i = 0; i < call.ArgCount; i++) {
if (i > 0) _property.LogMessage(", ");
_property.LogMessage(call.GetArgName(i) +
"= " + call.GetArg(i));
}
_property.LogMes sage(")n");
}
}
_replyMsg = _nextSink.SyncProcessMessage(reqMsg);
if (_replyMsg is IMethodReturnMessage) {
IMethodReturnMessage retMsg =
(IMethodReturnMessage) _replyMsg;
Exception e = retMsg.Exception;
if (e!= null) {
Console.WriteLine(e.Mes sage);
return _replyMsg;
}
lock(_property) {
_property.LogMessage("===" + retMsg.TypeName);
_property.LogMessage("n" + retMsg.MethodName +
" nt <<<OUT»> parameters: (");
for (int i = 0; i < retMsg.OutArgCount; i++) {
if (i > 0) _property.LogMessage(", ");
_property.LogMessage(retMsg.GetOutArgName(i) +
" = " + retMsg.GetOutArg(i));
}
_property.LogMes sage(")n");
}
}
return _replyMsg;
public virtual IMessageCtrl AsyncProcessMessage(IMessage msg,
IMessageSink replySink) {
throw new InvalidOperationExcept();
}
public IMessageSink NextSink {
get {
return _nextSink;
}
}
}
}
Комментарии к коду:
1. Данный код содержит определения двух классов:
♦ MyCallTraceAttribute
Этот публичный класс доступен всем приложениям, имеющим доступ к сборке MyServer.ехе
♦ MyCallTraceServerContextSink
Этот класс является внутренним (internal) для сборки MyServer.ехе и не доступен за ее пределами.
2. Классу MyCallTraceAttribute приписан атрибут [AttributeUsage (AttributeTargets. Class)]. Данный атрибут используется при определении пользовательских атрибутов для задания элементов, которым может быть приписан данный атрибут. В данном случае атрибут MyCallTraceAttribute можно приписать только классу (но нельзя приписать, например, какому-то методу).
3. Комментарии к коду класса MyCallTraceAttribute:
♦ Класс MyCallTraceAttribute является производным классом от класса ContextAttribute и реализует интерфейс IContributeServerContextSink. В свою очередь класс ContextAttribute реализует интерфейсы IContextProperty и IContextAttribute.
Реализация интерфейсов IContextProperty и IContextAttribute обеспечивает выбор контекста для размещения активируемого объекта (в старом или в новом контексте), а в случае формирования нового контекста — назначение ему свойств, которые объект может вызывать в своем коде явно.
Реализация интерфейса IContributeServerContextSink позволяет включить в конец цепочки перехватчиков, которые перехватывают все входящие в контекст вызовы, новый перехватчик. Это позволяет декларативно связать некоторый класс с некоторым автоматическим сервисом, что и является реализацией идей аспектно-ориентированного программирования.
♦ Константа PROPERTY_NAME будет использована для задания имени свойству контекста. Каждое свойство контекста имеет имя и для любого заданного контекста
Откройте для себя мир чтения на siteknig.com - месте, где каждая книга оживает прямо в браузере. Здесь вас уже ждёт произведение Интернет-журнал "Домашняя лаборатория", 2007 №6 - Усманов, относящееся к жанру Газеты и журналы / Сделай сам / Хобби и ремесла. Никаких регистраций, никаких преград - только вы и история, доступная в полном формате. Наш литературный портал создан для тех, кто любит комфорт: хотите читать с телефона - пожалуйста; предпочитаете ноутбук - идеально! Все книги открываются моментально и представлены полностью, без сокращений и скрытых страниц. Каталог жанров поможет вам быстро найти что-то по настроению: увлекательный роман, динамичное фэнтези, глубокую классику или лёгкое чтение перед сном. Мы ежедневно расширяем библиотеку, добавляя новые произведения, чтобы вам всегда было что открыть "на потом". Сегодня на siteknig.com доступно более 200000 книг - и каждая готова стать вашей новой любимой. Просто выбирайте, открывайте и наслаждайтесь чтением там, где вам удобно.


