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