авторефераты диссертаций БЕСПЛАТНАЯ БИБЛИОТЕКА РОССИИ

КОНФЕРЕНЦИИ, КНИГИ, ПОСОБИЯ, НАУЧНЫЕ ИЗДАНИЯ

<< ГЛАВНАЯ
АГРОИНЖЕНЕРИЯ
АСТРОНОМИЯ
БЕЗОПАСНОСТЬ
БИОЛОГИЯ
ЗЕМЛЯ
ИНФОРМАТИКА
ИСКУССТВОВЕДЕНИЕ
ИСТОРИЯ
КУЛЬТУРОЛОГИЯ
МАШИНОСТРОЕНИЕ
МЕДИЦИНА
МЕТАЛЛУРГИЯ
МЕХАНИКА
ПЕДАГОГИКА
ПОЛИТИКА
ПРИБОРОСТРОЕНИЕ
ПРОДОВОЛЬСТВИЕ
ПСИХОЛОГИЯ
РАДИОТЕХНИКА
СЕЛЬСКОЕ ХОЗЯЙСТВО
СОЦИОЛОГИЯ
СТРОИТЕЛЬСТВО
ТЕХНИЧЕСКИЕ НАУКИ
ТРАНСПОРТ
ФАРМАЦЕВТИКА
ФИЗИКА
ФИЗИОЛОГИЯ
ФИЛОЛОГИЯ
ФИЛОСОФИЯ
ХИМИЯ
ЭКОНОМИКА
ЭЛЕКТРОТЕХНИКА
ЭНЕРГЕТИКА
ЮРИСПРУДЕНЦИЯ
ЯЗЫКОЗНАНИЕ
РАЗНОЕ
КОНТАКТЫ


Pages:     | 1 || 3 |

«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ В. О. Сафонов АСПЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ Учебное пособие ...»

-- [ Страница 2 ] --

} Данное правило внедрения обеспечивает следующую функцио нальность. В целевом приложении, как предполагается в определении аспекта, должны быть классы, имена которых оканчиваются на BankAccount. В таких классах, как предполагается, должен быть стати ческий метод withdraw с одним аргументом типа float. В соответствии с данным правилом, все такие вызовы метода withdraw будут заменены на вызовы нового метода WithdrawWrapper (действия аспекта — «обертки» для метода withdraw), реализующего некоторые дополни тельные проверки, которые необходимо выполнить перед вызовом банковской операции снятия со счета (withdraw).

Ограничение args(..) означает, что все аргументы целевого метода withdraw будут захвачены и обработаны как соответствующие аргу менты действия аспекта. В соответствии с данным ограничением, дей ствие аспекта WithdrawWrapper также имеет ровно один аргумент типа float. Функциональность args реализована аналогично возможностям системы AspectJ.

Примеры фильтрации методов Возможность фильтрации методов по их сигнатурам — весьма важный и мощный инструмент. Вот несколько примеров фильтрации методов:

public static float *BankAccount.withdraw(float) float *BankAccount.withdraw(..) — любые аргументы private * *BankAccount.withdraw(float) private * *BankAccount.withdraw(float,..) -- первый аргумент — типа float, остальные аргументы могут быть произвольными Семантика Правила внедрения определяют, каким образом аспект в Aspect.NET вставляет вызовы действий аспекта перед точкой присое динения, после нее или вместо нее — точки (фрагмента) в коде целе вой программы, который подсистема внедрения находит, в соответст вии с условием правила.

В текущей версии — Aspect.NET 2.2 — реализована только разно видность call точек присоединения. Это означает, что подсистема вне дрения может вставлять вызовы действий аспекта перед вызовом, по сле или вместо вызова метода в целевой программе.

В последующих версиях мы также планируем реализовать две других разновидности точек присоединения — assign (присваивание именованной сущности) и use (использование именованной сущности).

Действие аспекта должно быть public static — методом.

Условие правила внедрения в общем случае может быть дизъюнк цией предложений:

c1 || … || cn Такое условие удовлетворяется, если удовлетворяется любая из его компонент. Например, условие:

%before %call *Set || %before %call *Get означает «перед вызовом любого метода, имя которого оканчивается на Set или Get.

Условие в простейшем случае представлено шаблоном для выбора имен методов (например, «*», что, как обычно, обозначает любое имя метода). Если искомый метод целевой программы принадлежит ее классу C, то для имени данного метода должен быть использован шаб лон C.* При использовании условия вида %instead (вместо) необходимо указывать только статические методы целевой программы, так как действие аспекта также является статическим методом. Иначе подсис тема внедрения выдаст сообщение об ошибке, и внедрение будет не возможно.

Условие может также содержать ограничения, отделяемые от шаблона для имени метода знаком &&. Это означает, что условие, за даваемое шаблоном имени метода, и данное ограничение рассматри ваются как конъюнкция условий.

Ограничение вида %arg указывает, сколько и какие именно аргу менты целевого метода будут захвачены действием аспекта. В нем конструкция IntLiteral специфицирует номер аргумента в целевой про грамме (нумерация начинается от 0) для захвата.

Например, если задано ограничение вида %arg(args[1]), соответ ствующее действие аспекта должно иметь один аргумент, тип которо го идентичен типу первого (начиная от 0) аргумента метода целевой программы. При вставке вызова действия аспекта, его аргумент дол жен быть первым аргументом целевого метода.

Если указано ограничение вида %arg(args[0],args[1]), то действие аспекта должно иметь два аргумента, типы котороых идентичны типам нулевого и первого аргументов целевого метода, и т. д.

Ограничение вида %arg(..) означает, что действие аспекта должно иметь то же число аргументов, что и целевой метод.

Таким образом, ограничения вида %arg предоставляют полезную возможность взаимодействия аспекта с контекстом его точек присое динения.

Ограничение вида %within(T) означает, что поиск целевых мето дов будет осуществляться только внутри определения типа T.

Ограничение вида %!within(T) означает, что поиск целевых мето дов будет осуществляться везде, кроме определения типа T.

Ограничение вида %withincode(M) означает, что поиск целевых методов будет осуществляться только внутри кода методов, имена ко торых удовлетворяют шаблону M.

Ограничение вида %!withincode(M) означает, что поиск целевых методов будет осуществляться везде, кроме кода методов, имена кото рых удовлетворяют шаблону M. Например, ограничение %!withincode(*.ctor) означает, что поиск целевых методов будет осу ществляться везде, кроме кода конструкторов.

5.4. Захват информации из контекста точки присоединения Aspect.NET 2.2 обеспечивает целый ряд способов коммуникации кода аспекта с контекстом его точек присоединения.

Первый из них, ограничения вида %args, описан в предвдущем пункте Он обеспечивает доступ к аргументам целевого метода и ана логичен возможностям AspectJ.

Второй способ коммуникации — это набор простых конструкций метаязыка Aspect.NET.ML, которые могут быть использованы в коде определения аспекта и обеспечивают доступ к имени целевого метода, номеру строчки исходного кода, где расположена точка присоедине ния и т. д.

Синтаксис TargetAppContextItem = %TargetObject | %TargetMemberInfo | %This | %RetValue | %WithinType | %WithinMethod | %SourceFilePath | %SourceFileLine.

Семантика Простые конструкции Aspect.NET.ML, перечисленные выше, мо гут использоваться в определении аспекта на Aspect.NET.ML, везде, где может быть использовано первичное выражение языка C#, за ис ключением левых частей присваиваний, т. е. указанные сущности не могут быть модифицированы аспектом. Они предоставляют базовые способы коммуникации аспекта с его точками присоединения.

%TargetObject (типа System.Object) — это ссылка на объект в це левой программе, к которому применяется целевой метод в точке при соединения. Например, если вызов целевого метода в точке присоеди нения имеет вид p.M(), то %TargetObject обозначает ссмылку на объ ект p. Если целевой метод статический, %TargetObject равно null.

%TargetMemberInfo (типа System.Reflection.MemberInfo) — это ссылка на объект, представляющий целевой метод из точки присоеди нения в традиционном стиле, принятом при использовании рефлексии.NET (System.Reflection). Данная конструкция позволяет получить зна чительный объем информации о целевом методе и использовать ее в аспекте. В частности, %TargetMemberInfo.Name — это public-свойство, содержащее имя целевого метода.

%RetValue (типа System.object) — это значение, возвращаемое це левым методом. Пример его использования — RetValue, входящий в поставку Aspect.NET 2.1.

%WithinType (типа System.Type) — это ссылка на определение ти па, в котором расположен вызов целевого метода.

%WithinMethod (типа System.Reflection.MethodBase) — это ссылка на объект, представляющий метод, в котором расположен вызов целе вого метода.

%SourceFilePath — это строка, представляющая путь к целевому файлу исходного кода, в клотором расположен вызов целевого метода.

%SourceFileLine — это строка, представляющая номер строчки в исходном коде, в файле, где расположен вызов целевого метода.

%This — это ссылка на объект, представляющий аспект в целом.

В соответствии с реализацией Aspect 2.2, каждый аспект представлен дочерним классом абстрактного класса Aspect, определенного в про странстве имен AspectDotNet, части реализации Aspect.NET. Родствен ная связь класса, задающего определение аспекта, и класса Aspect по исходному коду.an.cs — файла, генерируемого конвертором Aspect.NET. Все другие ключевые слова Aspect.NET.ML, описанные в данном пункте, реализованы как public static — свойства класса, реа лизующего аспект.

5.5. Самодокументирование аспектов: AspectDescription Исходный код определения аспекта, может быть документирован обычными комментариями в стиле // (от символа // до конца строчки исходного кода).

Aspect.NET обеспечивает преобразование таких комментариев в значение специализированного атрибута AspectDescription. Более под робно: если комментаврий в стиле «//» дан перед заголовком аспекта, либо перед действием аспекта, то конвертор Aspect.NET.ML преобра зует такие комментарии, как показано в приведенном ниже примере.

Код на Aspect.NET.ML:

// This is my aspect %aspect MyAspect { // This is my aspect’s action %before %call * %action public static void A() { Console.WriteLine("Action A");

} … Соответствующий код на C# после конвертирования:

[AspectDescription("This is my aspect")] public class Aspect1 : Aspect { [AspectDescription(«This is my aspect’s action»)] [AspectAction("%before %call *")] public static void A() { Console.WriteLine("Action A");

} … Самодокументирующие описания аспектов полезны тем, что они воспроизводятся Aspect.NET Framework при визуализации открытого аспекта в окне Aspect.NET, что улучшает понимаемость аспектов их пользователями.

5.6. Использование специализированных атрибутов Aspect.NET непосредственно Как объяснено выше, Aspect.NET позволяет избежать использова ния метаязыка Aspect.NET.ML для реализации аспектов и вместо этого использовать непосредственно код на языке C# с определениями спе циализированных атрибутов АОП.

Имеется два специализированных атрибута АОП для представле ния аспектов — AspectDescription и AspectAction.

Атрибут AspectDescription содержит определенную пользователем строку, описывающую (аннотирующую) определение аспекта в целом и его действия. Данная строка воспроизводится компонентой Aspect.NET Framework при визуализации информации об аспекте.

Данный специализированный атрибут не является обязательным. Оп ределение аспекта может иметь либо не иметь явных пользовательских описаний (в виде строк) аспекта и его действий, хотя их наличие и ре комендуется.

Атрибут AspectAction — ключевой. Он помечает любое действие аспекта и содержит (в форме строки) соответствующее условие вне дрения. Данный атрибут — обязательный для аспектов в Aspect.NET.

Если методы, реализующие действия аспекта, не помечены данным атрибутом, Aspect.NET «не поймет» правильно структуру аспекта, и Aspect.NET Framework не позволит загрузить данную сборку как ас пект.

Использование описанных атрибутов АОП иллюстрируется сле дующим примером кода на C#:

namespace BankManagement { [AspectDescription ("Design By Contract aspect")] public class BankAccountContractAspect: Aspect { [AspectDescription ("Invariant: this.Balance = 0")] [AspectAction("%before %call*BankAccount.*(..) || %after %call *BankAccount.*(..)")] static public void CheckInvariant() { if (((BankAccount)TargetObject).Balance 0) Console.WriteLine ("ERROR INVARIANT: Balance should "+ "not be negative at {0} line {1}", SourceFilePath, SourceFileLine);

} } } Заметим. что при непосредственном использовании атрибутов АОП не следует забывать явно указывать наследование от класса Aspect. Иначе Aspect.NET Framework «не поймет» Ваш код как опре деление аспекта.

Вопросы к главе 1. Каково назначение метаязыка Aspect.NET.ML?

2. Каким образом система Aspect.NET обрабатывает код на метаязыке Aspect.NET.ML?

3. Какова структура определения аспекта в Aspect.NET.ML?

4. Каковы возможные виды точек присоединения?

5. Что такое данные, модули, действия и правила внедрения аспекта?

6. Каким образом задается фильтрация аргументов целевых методов?

7. Какие метапонятия (ключевые слова) используются для обработки контекста точки присоединения?

8. Каким образом можео быть определен аспект без использования ме таязыка Aspect.NET.ML?

Упражнения к главе 1. Разработайте аспект протоколирования, выдающий перед вызовом каждого метода сообщение о начале его работы, а после вызова — сообщение об окончании работы метода. Сообщения должны вклю чать имена методов.

2. Разработайте аспект, который ищет в целевом приложении все мето ды с одним параметром типа int и выводит перед вызовом таких ме тодов сообщение о начале их работы, включающее имя метода и зна чение фактического параметра.

3. Напишите условие фильтрации целевых методов, которое позволяет найти в целевой программе вызовы всех методов, имена которых на чинаются на Set, с тремя аргументами — целым (int), вещественным (double) и булевским (boolean).

Глава ПРАКТИЧЕСКОЕ ИСПОЛЬЗОВАНИЕ СИСТЕМЫ ASPECT.NET 6.1. Пример целевой программы и определения аспекта Перед инсталляцией и использованием Aspect.NET внимательно прочитайте раздел 6.4 (в конце данной главы), в котором перечислены все требования к окружению, и строго соблюдайте их.

Рассмотрим простой пример целевой программы и определения аспекта, предназначенного для внедрения в нее. Опишем последова тельно все этапы определения аспекта, его внедрения с помощью Aspect.NET, и тестирования функциональности измененной целевой программы после внедрения аспекта, по сравнению с ее первоначаль ной версией. Данного примера вполне достаточно, чтобы изучить ос новы работы в системе Aspect.NET Framework.

1) Рассмотрим и введем в редакторе Visual Studio простую кон сольную программу на языке C#:

using System;

using System.Collections.Generic;

using System.Text;

namespace ConsoleApplication { class Program { static void P() { Console.WriteLine("P");

} static void Main(string[] args) { Console.WriteLine("Main");

P();

} } } Предположим, что мы создали в Visual Studio.NET 2005 решение (solution) для этой программы и назвали его ConsoleApplication1.

Выполним сборку (build) и запустим нашу программу.

Ее вывод:

Main P 2) После инсталляции Aspect.NET «поверх» Visual Studio.NET 2005, как рекомендовано выше, создадим теперь простой аспект, цель которого — вставка в целевую программу протоколирующих сообще ний (logging messages) вида:

Hello P и Bye P соответственно, перед вызовом каждого метода P и после его вызова в целевой программе (выражаясь более точно, в сообщении будем выво дить имя и заголовок целевого метода).

Таким образом, данный аспект, в некотором смысле, делает целе вую программу более «вежливой».

Для создания решения (solution), в котором будет определен наш аспект, вызовем Visual Studio.NET 2005 и выберем в основном меню пункт File / New / Project. Заметим при этом, что окно Aspect.NET Framework появляется на экране как часть GUI Visual Studio.NET, как показано на рис. 6.1.

Выберем вид проекта «Aspect.NET ML module» (модуль на мета языке Aspect.NET.ML).

Заметим, что имеется и другая разновидность проекта — Aspect.NET module (модуль Aspect.NET). Ее следует выбрать, если мы решим обойтись без метаязыка Aspect.NET.ML и использовать спе циализированные атрибуты АОП непосредственно на языке C#.

Предположим, что имя аспектного проекта — Aspect1 (оно выби рается по умолчанию).

Рис. 6.1. Создание проекта «аспект» в Visual Studio.NET Aspect.NET Framework сгенерирует следующий шаблон опреде ления аспекта:

%aspect Aspect using System;

class Aspect { %modules %rules } который будет записан в файл с именем (по умолчанию) Aspect.an.

Для имен файлов с кодом на метаязыке Aspect.NET.ML использу ется расширение «.an».

Теперь введем полный исходный код аспекта, приведенный ниже:

%aspect Aspect using System;

{ %modules public static void Say (String s) { Console.WriteLine(s);

} %rules %before %call * %action public static void SayHello () { Say ("Hello " + %TargetMemberInfo.ToString());

} %after %call * %action public static void SayBye () { Say ("Bye " + %TargetMemberInfo.ToString());

} } Аспект Aspect1 состоит из:

заголовка аспекта — %aspect Aspect1. Конвертор Aspect.NET преобразует его в заголовок класса с именем Aspect1. До пустимо также явное использование заголовка класса Aspect1 после заголовка аспекта. В таком случае заголовок класса не будет сгенери рован;

раздела modules, в котором необходимо определить модули аспекта, которые, как правило, должны быть public static методами;

раздела rules, в котором указываются правила внедрения ас пекта. Каждое правило внедрения состоит из условия (%before %call *, %after %call *) и действия. Условие используется для поиска множе ства потенциальных точек присоединения (join points) в целевой про грамме, в которые должно быть выполнено внедрение аспекта (перед вызовом каждого метода целевой программы или после его вызова, соответственно). Множество имен методов специфицировано с помо щью традиционного шаблона «*» («все»). Действие должно быть оп ределено как public static — метод.

Для протоколирования фактического имени и заголовка метода целевой программы в каждой точке присоединения используется кон струкция %TargetMemberInfo метаязыка Aspect.NET.ML. Она преобра зуется конвертором в конструкцию языка C# для доступа к имени и сигнатуре целевого метода.

3) Теперь соберем (build) решение Aspect1. Заметим, что в ходе сборки создан новый текстовый файл Aspect1.an.cs с определением аспекта на языке C#, который автоматически добавлен к набору фай лов решения:

//---------------------------------------------- // auto-generated // This code was generated by a tool.

// Runtime Version:2.0.50727. // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated.

// /auto-generated //---------------------------------------------- namespace Aspect1 { using System;

using AspectDotNet;

public class Aspect1 : Aspect { public static void Say (String s) { Console.WriteLine(s);

} [AspectAction("%before %call *")] public static void SayHello () { Say ("Hello " + TargetMemberInfo.ToString());

} [AspectAction("%after %call *")] public static void SayBye () { Say ("Bye " + TargetMemberInfo.ToString());

} } } Проанализируем полученный код. На уровне языка реализации C# аспект представлен определением класса, а каждое действие помечено специализированным атрибутом AspectAction, содержащим условие внедрения и используемым системой Aspect.NET.

Если пользователю так удобнее, аспект может быть определен не посредственно на C#, без использования метаязыка Aspect.NET.ML. В таком случае следует выбрать другой вид проекта — Aspect.NET module, для которого Aspect.NET предлагает следующий шаблон ис ходного кода на C#:

using System;

using System.Collections.Generic;

using System.Text;

using AspectDotNet;

namespace Aspect { [AspectDescription("MyAspect description")] public class MyAspect : Aspect {} } 4) Чтобы теперь внедрить аспект Aspect1 в программу откроем в Visual Studio решение ConsoleApplication, ConsoleApplication1 (см. рис. 6.2):

Рис. 6.2. Открытие целевой программы и окна Aspect.NET Вы увидите окно Aspect.NET Framework.

Затем выберем DLL-библиотеку аспекта Aspect1, используя икон ку «Open», расположенную на вкладке Aspects» tab. Как правило, DLL библиотека, являющаяся представлением аспекта в виде двоичного кода, записывается системой Visual Studio.NET в поддиректорию bin\debug решения, представляющего аспект.

В результате увидим компоненты открытого аспекта — имя и действия, визуализируемые Aspect.NET (рис. 6.3).

После этого необходимо вызвать подсистему внедрения аспектов (weaver) в режиме сканирования (поиска) точек присоединения. Это делается нажатием кнопки Find Joinpoints. В консоли вывода будет отображаться трассировка работы weaver’а.

Рис. 6.3. Визуализация открытого аспекта Следует отметить, что внедрение аспектов в Aspect.NET осущест вляется на уровне сборки (.NET assembly), содержащей MSIL-код и метаданные. В этом отношении Aspect.NET отличается от многих дру гих инструментов АОП. Результирующая сборка с внедрненными ас пектами может быть использована самым обычным образом, напри мер, быть дизассемблированной утилитой ildasm или отлаживаться штатным отладчиком.NET.

Вернемся к нашему примеру. После завершения работы подсис темы внедрения на вкладке Joinpoints будут визуализированы потен циальные точки присоединения аспекта (см. рис. 6.4).

На данном этапе имеется возможность «вручную» отметить ка кую-либо потенциальную точку присоединения, либо отменить ее от метку, исключив тем самым ее из дальнейшего процесса внедрения.

Это позволяет избежать «слепого», неконтролируемого внедрения ас пектов — одной из самых серьезных опасностей, которые таит в себе АОП — мощное «оружие», управляющее групповыми модификациями кода. Описываемая возможность Aspect.NET — внедрение, управляе мое пользователем, — позволяет сделать процесс внедрения аспектов более безопасным и разумным. Отметим еще раз, что подобная воз можность отсутствует во всех известных нам остальных, даже весь ма популярных, инструментах АОП.

Рис. 6.4. Визуализация точек присоединения, найденных подсистемой внедрения аспектов На данном этапе имеется также возможность визуализации каж дой потенциальной точки присоединения аспекта в исходном коде це левой программы. Для этого следует использовать вкладку Visuali zation, щелкнув на соответствующем красном отрезке, схематически изображающем точку присоединения.

Наконец, для внедрения аспекта следует нажать кнопку Weave aspects. При этом вызовется weaver в режиме внедрения.

На консоли вывода будет выдаваться трассировка его работы.

После завершения внедрения аспекта Вам будет предложено од ним нажатием кнопки сразу же запустить модифицированный код це левой программы.

Модифицированный код с внедренными аспектами будет записан системой Aspect.NET в ту же директорию, в которой расположен и исходный бинарный код целевой программы. Имя модифицированно го файла кода имеет вид: ~TargetAppName, в нашем примере:

~ConsoleApplication Вывод модифицированной программы:

Hello Void WriteLine(System.String) Main Bye Void WriteLine(System.String) Hello Void P() Hello Void WriteLine(System.String) P Bye Void WriteLine(System.String) Bye Void P() Далее Вы можете выгрузить из Aspect.NET сборку аспекта, загру зить сборку другого аспекта, загрузить заново все сборки всех аспек тов, уже известных Aspect.NET, и управлять структурой списка (оче реди) открытых аспектов, используя соответствующие иконки на вкладке Aspects.

6.2. Опции системы Aspect.NET Опции Aspect.NET Framework могут быть установлены после вы бора в главном меню пункта Tools / Options / Aspect.NET Framework (см. рис. 6.5).

Рис. 6.5. Опции Aspect.NET Как видно из рисунка, по умолчанию Aspect.NET 2.1 генерирует новые специализированные атрибуты АОП — AspectDescription (Ваши комментарии к аспектам и их частям) и AspectAction (атрибут, поме чающий действия аспекта).

Для обратной совместимости с предыдущими версиями Aspect.NET (1.0, 1.1), могут быть использованы «старые» определения аспектов, созданные с помощью этих предыдущих версий. При этом они не должны использовать характерное для версий 1.0 и 1.1 времен ное соглашение о связях между действием аспекта и контекстом точки присоединения через аргументы действия. Если выбрать пункт AspectDef (version 1.0), то, Aspect.NET — как конвертор, так и weaver, — будет работать в режиме совместимости с версиями 1.x и использовать «старый» специализированный атрибут AspectDef.

Наконец, фаза конвертирования из Aspect.NET.ML на C# может быть явным образом отменена с помощью опций Aspect.NET, в ре зультате чего Вы сможете использовать непосредственно язык C# со специализированными атрибутами и соответствующий тип проекта — Aspect.NET module — для определения аспектов.

6.3. Аспекты, входящие в поставку Aspect.NET 2. С версией Aspect.NET 2.2 поставляются следующие примеры ас пектов и целевых программ для их внедрения, которые мы предлагаем читателям подробно изучить и пропустить самостоятельно:

Aspect2 — простой пример определения аспекта, демонстрирую щий основные понятия АОП и Aspect.NET и использование %TargetMemberInfo. Он аналогичен рассмотренному выше примеру ConsoleApplication1 / Aspect1.

Пример состоит из целевой программы ConsoleApplication1 и ас пекта Aspect2, внедряемого в нее.

TestArgs — аспект, демонстрирующий фильтрацию целевых мето дов и захват аргументов целевого метода. Состоит из целевой про граммы TestArgs и аспекта TestArgsAspect.

BankManagement (автор Дмитрий Григорьев) — упрощенная про грамма для управления банковскими счетами и два аспекта, расши ряющие ее возможности некоторыми дополнительными проверками, связанными с безопасностью и лицензированием: например, баланс должен быть больше или равен нулю, и т. д. Данный пример может также рассматриваться как демонстрация возможностей контрактного (Design-by-Contract) стиля программирования, который обеспечивает повышенную надежность программ. Пример состоит из целевой про граммы BankManagement, класса BankAccount, испорльзуемого в дан ной программе, и двух аспектов — BankAccountContractAspect и UsageLicensingAspect. Для демонстрационных целей примеры этих аспектов написаны непосредственно на C# с явным использованием спекциализированных атрибутов АОП (фаза конвертирования пропус кается). Отметим, что Вы можете загрузить оба аспекта в Aspect.NET Framework в одном вызове (экземпляре) Visual Studio и выполнить цикл «Find Join Points / Weave Aspects» лишь один раз, одним вызовом подсистемы внедрения аспектов Aspect.NET.

MAddNopCounter (автор Дмитрий Григорьев) — наш «подарок» от группы Aspect.NET группе Phoenix из Microsoft. Целевая программа в данном примере — это программа addnop-tool из примеров, постав ляемых с версией Phoenix RDK, расположенная в поддиректории src/samples/AddNop-tool/csharp Phoenix RDK (март 2007). Эта про грамма, использующая Phoenix, вставляет инструкцию nop (пустую операцию) после каждой инструкции MSIL-кода файла сборки.NET, заданного параметром. Но утилита addnop-tool слишком «молчалива».

В нашем примере, в дополнение к ней, мы предлагаем аспект MAddNopCounter, внедрение которого в программу addnop-tool расши ряет ее функциональность возможностями «инструментовки» ее кода добавлением дополнительных операторов вывода, например, аспект подсчитывает и выводит фактическое число вставленных инструкций nop. Программа вызывается с двумя аргументами командной строки — имя исходного файла сборки и имя файла результата.

RetTest and RetTest2– примеры, демонстрирующие новую возмож ность Aspect.NET 2.2 — %RetValue, а также конструкции WithinType / WithinMethod (см. главу 5).

TestArgsVB — пример, демонстрирующий многоязыковое АОП:

исходная целевая программа на C# аналогична программе из примера TestArgs. Обрабатывающий ее аспект написан на языке Visual Basic.

Каждый из примеров состоит из определений аспектов и целевых программ для их внедрения. Аспекты самодокументированы с исполь зованием комментариев и атрибута AspectDescription.

Решения Visual Studio 2005 для аспектов и их целевых программ размещены в поддиректориях, имена которых совпадают с именами примеров.

6.4. Требования к окружению для инсталляции и использования Aspect.NET Система Aspect.NET 2.2 предназначена для использования в среде Visual Studio.NET 2005 и.NET 2.0 под управлением операционной системы Windows XP с Service Pack 2 (3).

Именно такое окружение было стандартным при разработке те кущей версии Aspect.NET.

Данное обстоятельство следует с самого начала иметь в виду пользователям, которые хотели бы использовать Aspect.NET 2.2 с Windows Vista или Windows 7, либо в среде Visual Studio.NET 2010 и т. д., поэтому экспериментировать с использованием Aspect.NET 2.2 в этих более новых системах не рекомендуется, во избежание потери времени.

Безусловно, в следующих версиях Aspect.NET (в 2011 г.) плани руется обеспечить совместимость с Windows Vista, Windows 7 и Visual Studio.NET 2010.

Система Aspect.NET Framework реализована как расширение (add-in) Visual Studio.NET 2005.

Это означает, что пользователи могут применять Aspect.NET как часть интегрированной среды Visual Studio.NET, с ее многочисленны ми возможностями (сборка, отладка, профилирование и др.) для разра ботки программного обеспечения.

На практике это означает, что какого-либо отдельного запуска Aspect.NET не требуется. Система Aspect.NET запускается вместе с Visual Studio, а ее графический пользовательский интерфейс (GUI), называемый Aspect.NET Framework, может рассматриваться как часть (расширенного) GUI интегрированной среды Visual Studio.NET.

Для того чтобы использовать Aspect.NET 2.2, необходимо предва рительно инсталлировать следующие системы, пакеты и инструменты:

1) ОС Windows XP с Service Pack 2 или Service Pack 2) Visual Studio.NET 2005 — либо Professional Edition, либо Stan dard Edition 3) Microsoft Platform Research Development Kit (RDK) Codenamed «Phoenix» March 2007 Release, доступный для бесплатного скачивания (точная ссылка дана на сайте проекта Aspect.NET) После этого может быть инсталлирована система Aspect.NET 2.2, дистрибутив которой вместе с примерами и документацией доступен в виде инсталляционного пакета.msi на сайте проекта.

Пожалуйста, будьте особенно внимательны относительно исполь зуемой версии Phoenix и установок, необходимых для его работы:

— используйте для Aspect.NET 2.2 только версию Phoenix RDK March 2007, которая на данный момент не является самой новой вер сией Phoenix (хотя и являлась ею на момент разработки первых версий Aspect.NET). Если Вы попытаетесь воспользоваться наиболее новой на данный момент версией Phoenix SDK (апрель 2008), Aspect.NET 2.2 с ней работать не будет. Перенос Aspect.NET на новую версию Phoenix — так же, как и ее перенос на новые версии Windows и Visual Studio, — значительная работа, которая планируется на ближайшее время. В настоящее время разработана экспериментальная версия под системы применения аспектов (weaver), совместимая с Phoenix SDK (апрель 2008), однако поставка данной версии пользователям будет осуществлена вместе с версией Aspect.NET Framework, совместимой с Visual Studio 2010.

Для надежной работы версии Phoenix RDK March 2007 добавьте к значению переменной окружения PATH следующий путь:

C:\Program Files\Microsoft Visual Studio 8\Common7\IDE, либо замените «C» обозначением другого логического раздела, если Visual Studio на Вашем компьютере установлена на логическом диске, отличном от C: ).

Еще раз обращаю Ваше внимание на то, что любое нарушение описанных выше условий, необходимых для успешной и надежной работы с Aspect.NET, неизбежно приведет к ненужным проблемам.

Мне приходилось многократно отвечать на однотипные письма поль зователей из различных стран с одними и теми же вопросами и про блемами, вызванными лишь их же собственной невнимательностью при чтении руководства пользователя и содержимого сайта Aspect.NET 2.2. Надеюсь, что пользователи из СПбГУ окажутся более внимательными.

Если по ошибке Вы инсталлировали другую версию Phoenix и (или) другую версию Aspect.NET, настоятельно рекомендую деин сталлировать Aspect.NET, затем Phoenix, затем инсталлировать «пра вильную» версию Phoenix и «правильную» версию Aspect.NET. Тогда никаких проблем с Aspect.NET возникнуть не должно.

Правильность инсталляции легко проверить путем пропуска само го простого примера, описанного ниже, входящего в поставку нашей системы.

Вопросы к главе 1. С какой версией Visual Studio совместима версия 2.2 системы Aspect.NET?

2. Что такое Phoenix и какую его версию необходимо использовать с Aspect.NET 2.2?

3. Требуется ли отдельный запуск Aspect.NET после запуска Visual Studio?

4. Какие действия необходимо предпринять, чтобы создать в Visual Studio.NET проект, определяющий новый аспект? Какие два способа (и, соответственно, какие два типа проектов) существуют для этого в среде Visual Studio, расширенной Aspect.NET 2.2?

5. Какие действия необходимы, чтобы в уже существующее приложе ние, определенное как решение (solution) Visual Studio, внедрить не который аспект с помощью системы Aspect.NET?

6. Каким образом можно управлять процессом внедрения аспектов, от меняя выбор тех или иных точек присоединения?

7. Каким образом можно управлять опциями Aspect.NET?

8. Каким образом можно визуализировать точки присоединения и ас пекты?

Упражнения к главе 1. Изучите и пропустите все примеры целевых программ и аспектов из поставки Aspect.NET 2.2 и с сайта проекта Aspect.NET.

2. Поэкспериментируйте с функциональностью внедрение, управляемое пользователем: попробуйте для примера Aspect2 перед внедрением аспекта отменять выбор то одной, то другой потенциальной точки присоединения и проследите, как изменится функциональность мо дифицированной целевой программы.

3. Модифицируйте пример TestArgsVB таким образом, чтобы:

целевая программа была написана на Visual Basic аспект был написан на C#.

Пропустите полученные модифицированные примеры.

4. Поэкспериментируйте с опциями системы Aspect.NET: отметьте пункт «Disable Aspect.NET.ML converter» и убедитесь в том, что кон вертор больше не участвует в качестве первого шага в процессе сбор ки.

5. Поэкспериментируйте с Aspect.NET GUI: закройте текущий откры тый аспект и откройте другой.

6. Поэкспериментируйте с логикой реализации кнопок Find Joinpoints и Weave Aspects: попробуйте нажать кнопку Find Joinpoints, если от крытых аспектов нет;

попробуйте нажать кнопку Weave Aspects, если еще не выполено действие Find Jopinpoints.

7. Поэкспериментируйте с визуализацией аспектов: визуализируйте в схематическом виде (с «раскрашиванием» разных аспектов в разные цвета) целевую программы с внедренными аспектами.

Глава ИСПОЛЬЗОВАНИЕ АОП ДЛЯ НАДЕЖНЫХ И БЕЗОПАСНЫХ ВЫЧИСЛЕНИЙ (TRUSTWORTHY COMPUTING) 7.1. Инициатива Trustworthy Computing фирмы Microsoft Одним из самых важных и перспективных направлений ИТ являет ся организация надежных и безопасных вычислений. В более широком смысле, в современной англоязычной терминологии, данный термин звучит как trustworthy computing, что в буквальном переводе означает вычисления, заслуживающие доверия. Что это означает на практике?

Как известно, в течение всего развития развития программирова ния и программного обеспечения, при любых подходах к разработке программ, наиболее важными их качествами признавались работоспо собность и полезность (usability) — программа должна выполнять то, чего от нее ожидают, в заданных условиях, а также быть дружествен ной к пользователю — быть удобной, иметь комфортный пользова тельский интерфейс, «вести себя разумно», с точки зрения житейской и профессиональной логики. Только такая программа, на наш взгляд, заслуживает доверия пользователей, поэтому и ставлю эти качества на первое место при определении понятия trustworthy computing.

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

Другое важнейшее качество программы — надежность (reliability): программа должна вести себя разумно и предсказуемо в случае некорректных исходных данных и обеспечивать безотказную работу в течение как можно более долгого периода времени. Понятие надежности в ранних работах было принято количественно характери зовать в терминах Mean Time Between Failures (MTBF) — среднего времени наработки на отказ, т. е. среднего времени, в течение которо го система работает безотказно. В настоящее время требуются более детальные и точные количественные оценки и предсказания надежно сти, над которыми и ведется работа.

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

Четвертое. Фирма-разработчик программы должна обеспечивать целостность и корректность бизнеса (business integrity), связанного с разработкой и использованием программы. Данное качество имеет две стороны. Во-первых, это четкая организация сопровождения про граммы — быстрое исправление ошибок и быстрые ответы на вопросы пользователей, которые, в свою очередь, должны оценить хорошую организацию бизнеса, связанного с разработкой и сопровождением, и, как следствие, продолжать пользоваться программой и приносить ее разработчикам доход. Во-вторых, это контроль прозрачности, закон ности, корректности бизнеса компании-пользователя программы.

Имеется в виду, прежде всего, что программа должна подсказывать пользователю, как ее использовать корректно. Если использование программы связано с ведением какого-либо бизнеса — продаж, рекла мы, перечисления денежных средств и т. д., — то программа должна контролировать корректность и законность всех операций, которые пользователь выполняет с ее помощью, а в случае обнаружения каких либо отклонений от этого правила — предупреждать и консультиро вать пользователя, предотвращая его возможные несанкционирован ные и незаконные действия. В настоящее время в стадии разработки в корпорации Microsoft находятся интеллектуальные решения для биз неса, которые осуществляют контроль и консультирование в области законности бизнеса на основе баз знаний.

Четыре перечисленных качества программ — security, reliability, privacy, business integrity, которым в наше время придается особое зна чение, стали основой инициативы trustworthy computing (TWC) [1], провозглашенной в 2002 г. корпорацией Microsoft. В меморандуме TWC фирмы Microsoft они названы «четырьмя колоннами TWC» (four pillars). Качество usability мы добавляем к рассмотренной парадигме, исходя из соображений здравого смысла.

Главная цель инициативы TWC — обратить внимание фирм разработчиков программного обеспечения на особую важность ука занных качеств программ. Разработчики должны стремиться к тому, чтобы учитывать требования TWC, начиная с самых ранних этапов разработки и на каждом ее этапе.

Классической работой по TWC стала книга [49], в которой под робно, на содержательных примерах, объясняются принципы разра ботки безопасного кода. Книга имеет приложение на Web-сайте, где доступны для скачивания через Web полезные примеры безопасного кода. Книга доступна также в русском переводе под названием «Безо пасный код».

В мировой практике использования программного обеспечения Microsoft — операционных систем семейства Windows, пакета Microsoft Office, обозревателя Internet Explorer и др. — как широко известно, имели и имеют место постоянные попытки хакеров с помо щью злонамеренных программ (malicious software, или, коротко, malware) — вирусов (viruses), «червей» (worms), троянских программ, или троянов (Trojan programs), — использовать уязвимости (vulnerabilities) программного обеспечения Microsoft для широкомас штабных сетевых атак.

В ответ на это Microsoft буквально еженедельно выпускает обнов ления своих программных продуктов, основная часть которых — ис правление уязвимостей безопасности.

Необходимо отметить, что, на наш взгляд, все это отнюдь не сви детельствует о ненадежности программных продуктов Microsoft. Это подтверждает только лишь их широкую популярность. Надеюсь, что моя мысль читателю понятна.

Любопытно, что, по признанию самих экспертов Microsoft [49], корпорация поддерживает неофициальные контакты с хакерами — видимо, это наиболее надежный способ вовремя узнавать об их пла нах.

С целью коренного улучшения ситуации, фирма Microsoft разра ботала и применяет особую схему жизненного цикла разработки безо пасных программ (Security Development Lifecycle, SDL) [50]. Она явля ется результатом анализа серьезных уроков, извлеченных Microsoft из опыта эксплуатации предыдущих версий Windows, до Windows XP.

Для улучшения безопасности уже выпущенной и распространенной по всему миру ОС Windows 2000, разработанной еще до введения SDL, Microsoft пришлось срочно прервать дальнейшую разработку системы, «посадить за парты» всех ее разработчиков, обучить их в кратчайшие возможные сроки принципам разработки безопасных программ, а за тем за короткий срок исправить уже существующий, гигантский код операционной системы и распространить ее новую, более надежную и безопасную версию. В мировую историю программирования этот бес прецедентный шаг вошел под названием «security push» [49].

Чтобы избежать подобных дорогостоящих экстренных мер при разработке нового программного обеспечения, Microsoft рекомендует применять схему SDL — проектировать, реализовывать и тестиро вать реализацию подсистемы безопасности на каждом этапе жиз ненного цикла программы, т. е. заниматься безопасностью программы постоянно, начиная с самых ранних этапов ее разработки. В этом — основная суть схемы SDL.

Поскольку для практического осуществления данного принципа требуются высококлассные эксперты по компьютерной безопасности, которыми большинство инженеров-программистов, даже в Microsoft, к сожалению, отнюдь не являются, — Microsoft рекомендует в каждом проекте иметь небольшую группу экспертов по безопасности, либо хотя бы одного эксперта, возможно, приглашенного из другой компа нии, — «security buddy». Цель экспертной группы по безопасности — постоянный контроль соблюдения правил разработки безопасного кода на всех этапах разработки, консультации разрабочиков по безо пасности и т. д.

Важно отметить, что, согласно принципам TWC и SDL, безопас ность системы начинается еще до ее разработки. В документе, описы вающем требования (requirements) к системе, должны быть отдельны ми разделами сформулированы требования к безопасности, типичные возможные угрозы (threats) системе и методы их отражения (mitigation). Для осуществления этих принципов необходимо еще на начальных этапах разработки заниматься моделированием угроз (threat modeling) [51].

Вот некоторые важнейшие принципы разработки безопасного ко да, согласно TWC:

Принцип минимизации атакующей поверхности программы (minimizing the attack surface). Необходимо проектировать и реализо вывать программу так, чтобы хакеры имели как можно меньше воз можностей «взломать» данные работающей программы и вывести ее из строя.

Принцип минимальных привилегий (least privilege). В боль шинстве случаев разработка и отладка программ ведется разработчи ками «от имени администраторов», т. е. программа тестируется от имени пользователя, имеющего права администратора, например, пра во изменения реестра или других системных файлов. При попытке использовать такую программу от имени обычного пользователя могут возникнуть проблемы, связанные с отсутствием полномочий. Чтобы этого не случилось, программу необходимо обязательно тестировать от лица обычного непривилегированного пользователя. Функциональ ность программы не должна опираться на то, что пользователь имеет права администратора.

Принцип обеспечения безопасности по дизайну, по умолчанию, при развертывании (secure by design, by default, by deployment). Как уже говорилось, еще на этапе проектирования (дизайна) программы необходимо предусмотреть для нее меры безопасности. Программа должна быть реализована так, чтобы быть безопасной по умолчанию, т. е. все меры и проверки безопасности по умолчанию должны быть включены (даже если это приведет к некоторому замедлению про граммы или некоторым неудобствам для пользователя). Мы с вами хорошо знаем, что последнее означает на практике: ОС и браузер за прашивают дополнительные подтверждения от пользователя в случае потенциально небезопасных для системы действий — просмотра не знакомого сайта, скачивания и инсталляции чужих программ и др. Это характерно для новых версий Windows. Первоначально подобные пре досторожности могут раздражать пользователя, однако они вполне оправданы, так как предотвращают многие типичные атаки.

Мировое сообщество программистов, к сожалению, в целом пока скептически, осторожно и инертно относится к инициативе TWC. Ме жду тем, проблемы с безопасностью и с внешними атаками испыты вают программные продукты всех фирм-разработчиков (и тем боль шие, чем фирма известнее) и миллионы их пользователей. Microsoft своей инициативой TWC как бы подает пример остальным компаниям, индивидуальным разработчикам и пользователям, и теперь дело — за нами, за мировым сообществом программистов: мы должны эту ини циативу подхватить и развивать, во имя нашей общей пользы и безо пасности разрабатываемых и используемых нами программ.

Применительно к нам, университетским ИТ-специалистам, это означает, прежде всего, что мы должны учить наших студентов и по стоянно учиться сами принципам, технологиям и инструментам TWC и их практическому применению.

К сожалению, пока не только в России, но и во всем мире обуче ние TWC недостаточно распространено. Этим занимаются, главным образом, университеты и колледжи в США и Канаде, ориентирован ные на военные разработки, — исторически именно такого рода орга низации, по вполне понятным причинам, испытывают особый интерес к компьютерной безопасности.

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

Ответом же на это (не только в России), к сожалению, подчас являют ся лишь общие слова об укреплении безопасности, не подкрепленные достаточными знаниями и адекватными инструментами для борьбы с атаками.

В 2005 г. Microsoft Research объявила конкурс грантов «Trustworthy Computing Curriculum», целью которого была поддержка внедрения принципов и методов обучения TWC в мировую универси тетскую практику. Автору посчастливилось выиграть такой грант (другими победителями конкурса стали 14 коллег из университетов США). Результатом работы автора в 2006 — 2008 гг. по проекту TrustSPBU.NET [1], удостоенному гранта Microsoft Research, стало введение в учебные программы и в практику обучения на мат-мехе СПбГУ нового спецкурса «Инженерия надежных и безопасных про грамм» (в англоязычном варианте — «Secure Software Engineering») [1] для студентов 4 курса, а также расширение тематики следующих моих курсов принципами TWC:

«Разработка надежных компиляторов» («Trustworthy Compiler Development») [1, 52] — обновленный и расширенный спецкурс по компиляторам для студентов 5 курса;

«Надежное и безопасное программирование для платформы Microsoft.NET на языке C# « («Trustworthy Programming for.NET Platform in C#») [1] — обновленный и расширенный спецкурс для сту дентов 4 курса;

«Операционные системы и сети» («Operating Systems and Networking») [1, 53] — обновленный и расширенный обязательный курс (64 часа) для студентов 2 курса.

Как важный результат и промежуточный итог работы по проекту TrustSPBU.NET (как, впрочем, и по проекту Aspect.NET [1]), в 2008 г.

в США опубликована монография автора на английском языке [1] из дательством John Wiley & Sons. Она, кроме материалов по АОП и по системе Aspect.NET, содержит также подробное описание авторского подхода к преподаванию ИТ — ERATO (Experience, Retrospective, Analysis, Theory, Oncoming perspectives;

в русском варианте — ТРОПА:

Теория, Ретроспектива, Опыт, Перспективы, Анализ), учебные про граммы курсов автора и пояснения к ним.

Как уже отмечалось, одной из самых серьезных проблем развития TWC как научного направления является выработка количественных оценок (quantitative assessment) безопасности, риска при разработке и использовании программ, надежности программ. В этом отношении следует отметить работы [54, 55], благодаря которым, впервые в более чем 50-летней практике программирования, становится возможным использование реалистичных и практически ориентированных количе ственных оценок качеств и свойств программ, основополагающих для TWC. Методы таких оценок основаны на методах математической ста тистики.

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

7.2. Применение АОП для TWC Как выяснилось в результате исследований автора и ряда других специалистов, два новых, интересных и современных направления — АОП и TWC — очень тесно, самым коренным образом связаны между собой. Суть реализации принципов TWC (усиления надежности и безопасности программ) в большинстве случаев состоит в системати ческих групповых, «сквозных» добавлениях в существующий код ка ких-либо действий или проверок. Но ведь именно для этого и предна значено аспектно-ориентированное программирование!


Вопросам применения АОП для разработки надежных и безопас ных программ посвящена монография автора [1]. В ней же дан под робный обзор работ по TWC, АОП и применению АОП для TWC.

Рассмотрим лишь некоторые важные принципы и методы при менения АОП для TWC. Общая идея заключается в следующем. АОП обеспечивает возможность систематических групповых вставок или изменений кода, управляемых аспектами — специальными модуля ми кода, предназначенными для внедрения (weaving) в целевые про граммы.

Безопасность (security). Как уже отмечалось, для решения задачи усиления безопасности какой-либо программы, как правило, требуется добавление проверок безопасности (security checks), например:

аутентификации (authentication) — проверок имени и пароля пользователя, совпадение которых, как предполагается, гарантируют его аутентичность;

авторизации (authorization) — проверок наличия у пользова теля необходимых полномочий для выполнения тех или иных дейст вий, например, изменения файла или открытия сокета.

В качестве примера рассмотрим платформу.NET и следующую типичную задачу. Требуется вывести на консоль список всех IP адресов заданного компьютера в сети (host). Простая программа на языке C#, решающая эту задачу без учета требований безопасности (использующая установки безопасности по умолчанию), может иметь вид [1]:

using System.Net;

public class Utilities { public static void PrintHostIP(string hostName) { Console.WriteLine ("IP address of the host=" + Dns.GetHostEntry(hostName).AddressList[0]);

Console.WriteLine("Press ENTER to exit");

Console.ReadLine();

} static void Main(string[] args) { PrintHostIP(Dns.GetHostName());

} } Здесь используются пространство имен System.Net для сетевого программирования в.NET и содержащийся в нем класс Dns (от DNS — Domain Name Service).

Код достаточно прост, но не вполне безопасен.

В соответствии с принципами TWC, безопасный код должен явно запрашивать полномочия безопасности перед выполнением того или иного действия, в данном случае — запроса IP-адресов компьютера.

Представим себе теперь, что уже разработан код сетевой про граммы на C# (объемом в несколько миллионов строк), в которой по добные действия по запросу IP-адресов, реализованные методом PrintHostIP, встречаются буквально на каждом шагу. Однако в перво начальном варианте программы не были предусмотрены меры безо пасности — проверки правомочности запроса IP-адреса.

Как же быть? Средствами Visual Studio, практически вручную, просматривать и модифицировать весь этот огромный код, добавляя там, где необходимо (перед каждым вызовом метода PrintHostIP), про верки полномочий? Понятно, что подобное решение может привести к ошибкам: например, если в исходном коде всего, например, содержит ся 1001 вызов метода PrintHostIP, то очень велик риск, что при выпол нении таких чисто рутинных действий — однотипных многократных вставок кода проверки безопасности — какой-нибудь (например, сто первый) вызов метода PrintHostIP модифицирован не будет, — напри мер, если сотрудника, выполняющего эту работу, срочно вызвал на чальник, после чего он начисто забыл, что не выполнил всю работу до конца. Как предотвратить подобные ситуации?

Адекватным средством решения этой проблемы является приме нение АОП и нашей системы Aspect.NET. Опишем аспект, который выполнит эту работу автоматически, — перед каждым вызовом метода PrintHostIP вставит в целевую программу вызов другого метода — DemandDnsPermission, проверяющего наличие необходимых полномо чий:

%aspect Aspect using System;

using System.Net;

using System.Security.Permissions;

class Aspect { %modules private static void DemandDnsPermission() { DnsPermission p = new DnsPermission(PermissionState.Unrestricted);

p.Demand();

} %rules %before %call *.PrintHostIP(string) && args(..) %action public static void GetHostAction (string hostname) { Console.WriteLine("Hostname="+ hostname);

Console.WriteLine("Demand DNS permission");

DemandDnsPermission();

Console.WriteLine ("Demand DNS permission finished");

} // GetHostAction } // Aspect Все необходимые для понимания этого примера детали организа ции системы Aspect.NET и метаязыка АОП Aspect.NET.ML, на кото ром (в сочетании с языком C#) написан приведенный код, описаны в главах 4 и 5.

Приведенный аспект вставляет перед каждым вызовом метода PrintHostIP в целевую программу вызов метода GetHostAction, опреде ленного в аспекте, который требует от системы наличия необходимых полномочий (вызывая метод Demand — отсутствие полномочий при водит к бросанию исключения SecurityException), и выдает на консоль трассировочные сообщения, поясняющие ход работы программы.

Самое удобное, что код целевой программы будет модифициро ван автоматически, с помощью среды Aspect.NET Framework и вклад ки Aspects. Ни один из вызовов метода PrintHostIP (называемый, в терминологии АОП, точкой присоединения — join point) не будет «за быт». Более того, пользователю перед фактическим внедрением аспек та будет предоставлена возможность просмотреть все найденные по тенциальные точки присоединения и, если необходимо, отказаться от какой-либо из них.

Еще одна интересная и важная деталь — в том, что эти изменения поведения целевой программы (усиление ее безопасности) с помощью системы Aspect.NET могут быть произведены, даже если исходный код целевой программы недоступен, а имеется лишь ее двоичный код — сборка (assembly).

Этот простой, но содержательный пример достаточно наглядно поясняет силу и возможности АОП и системы Aspect.NET для реше ния задач TWC.

Аналогичным образом, путем определения и применения соответст вующих аспектов, могут быть решены другие типичные задачи TWC [1]:

обработка ошибок (error handling) — обработка исключений или кодов возврата;

вставка синхронизирующих вызовов (synchronization calls) для различных способов синхронизации процессов и потоков;

вставки кода, обеспечивающего безопасность выполнения кода в многопоточной среде (multi-threaded safety);

вставка проверок в стиле design-by-contract (проверки предусловий, постусловий и инвариантов);

вставка трассировочного кода (logging);

вставка кода, обеспечивающего криптование и декриптова ние информации, а также проверки полномочности обраще ния к какой-либо информации, для обеспечения ее конфиден циальности (privacy) для целей business integrity — вставка кода, исправляющего заявленные пользователями ошибки или реализующего необ ходимые расширения функциональности;

вставка других необходимых проверок для улучшения надеж ности программ — например, вставка проверок на ненулевые указатели, вставка проверок допустимых диапазонов для це лочисленных аргументов и т. д.

7.3. Перспективы применения АОП для TWC Чем больше исследований и практических разработок по TWC ве дется в настоящее время, тем более ясно, что значительная часть рабо ты еще впереди. Необходимы, прежде всего, научные основы количе ственной оценки TWC и удобные методы и инструменты их примене ния. Необходимы инструментальные средства, обеспечивающие про ектирование и реализацию надежного и безопасного кода и предот вращающие появление кода, чреватого уязвимостями и опасного с точки зрения возможных атак. Все мы уже убедились, что гораздо дольше, сложнее и дороже улучшать надежность и безопасность уже находящегося в широком использовании кода «задним числом», чем с самого начала писать правильный, надежный и безопасный код.

АОП — лишь одна из технологий, удобных для TWC. Другим подходом, применимым для TWC, как уже отмечалось, является ин женерия знаний, более точно — разработка и практическое использо вание баз знаний о надежности и безопасности программ и о методах разработки надежного и безопасного кода. Более простой подход — применение шаблонов кода (code patterns), позволяющее в некоторых, наиболее простых случаях, избежать ошибок, связанных с безопасно стью.

В идеале, «умный» программный инструмент сам должен подска зывать пользователю, что, например, код, который он набрал в тексто вом редакторе интегрированной среды, подвержен атаке «переполне ние буфера» (buffer overrun) [49]. Очевидно, что для реализации дан ной идеи могут понадобиться шаблоны правильного кода, методы ин женерии знаний и формализации семантики программ, методы доказа тельства корректности программ.

Вопросы к главе 1. Что такое надежные и безопасные вычисления (trustworthy computing)? Почему они столь важны для успешной разработки и со провождения программ?

2. Сформулируйте принципы «четырех колонн» TWC — безопасности, надежности, конфиденциальности, корректности и оперативности бизнеса.

3. В чем близость АОП и TWC? Почему АОП хорошо подходит для TWC?

4. Каковы принципы разработки безопасных программ согласно модели TWC (безопасность по умолчанию и др.)?

Упражнения к главе 1. Изучите и пропустите все примеры целевых программ и аспектов TWC из книги [1]. Исходные коды и решения (solutions) Visual Studio для этих примеров доступны на сайте проекта Aspect.NET http://www.aspectdotnet.org.

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

3. Разработайте аспект, который проверяет наличие у целевого прило жения полномочий безопасности по открытию файлов (перед вызова ми метода Open).

4. Разработайте аспект, обеспечивающий криптование заданного тек стового файла каким-либо известным Вам методом перед вызовом метода Transfer, который, как предполагается, выполняет передачу файла через сеть.


5. Разработайте аспект, проверяющий перед пересылкой строк методом StringCopy (string destination, string source), что длина строки источника не больше длины строки-получателя.

Глава ПРИМЕНЕНИЕ АОП ДЛЯ КОНТРАКТНОГО ПРОЕКТИРОВАНИЯ (DESIGN BY CONTRACT) 8.1. Обзор технологии контрактного проектирования Материал данной главы основан на совместных исследователь ских работах автора и аспирантки А. Когай [17], за что автор выражает ей искреннюю благодарность.

Для облегчения и систематизации разработки больших объектно ориентированных программных систем, систематического подхода к спецификации и реализации классов и их взаимосвязей в программной системе, Бертраном Мейером в конце 1980-х гг. был предложен под ход, получивший название контрактное проектирование (design by contract) [57]. При данном подходе программная система рассматрива ется в виде множества взаимодействующих компонент, взаимоотно шения между которыми строятся на основе точно определенной спе цификации взаимных обязательств — контрактов. Мейер предложил использовать спецификации контрактов в рамках разработанного им языка программирования Eiffel [57]. Кроме Eiffel, встроенная под держка технологии design-by-contract имеется в языках программиро вания Сhrome, D, Lisaac, Nemerle и др. Если встроенная поддержка отсутствует, то при попытке применять принципы контрактного про ектирования обычными средствами возникают определенные трудно сти. Аспектно-ориентированное программирование, как показано в данной главе, является одним из возможных решений проблемы при мерения технологии design-by-contract.

По Мейеру [57], корректность программной системы или ее эле мента могут бытьисследованы по отношению к некоторой специфика ции. Термин «корректность» не применим к программному элементу самому по себе, он имеет смысл лишь для пары: (программный эле мент, его спецификация). Следовательно, первым и важнейшим шагом на пути к корректности является разработка спецификаций программ ных элементов — точных определений того, что именно рассматри ваемый этот программный элемент должен выполнять. Спецификации должны разрабатываться одновременно с написанием программы, при некоторых подходах — до ее написания. Конечно, наличие специфи кации не гарантирует, что программа будет работать в полном соот ветствии с ней. Однако без спецификации, определяющей функцио нальность модуля, вероятность того, что он будет реализован правиль но, невысока.

Следствия данного подхода:

Программная система с самого начала проектируется так, что бы быть корректной. В момент написания программа снабжа ется аргументами, характеризующими ее корректность.

Значительно проясняется представление проблемы (задачи) и возможных путей ее развития.

Упрощается задача разработки программной документации.

Обеспечиваются основы для систематического тестирования и отладки.

Методология контрактного проектирования предполагает приме нение спецификации к каждому программному элементу. Эти специ фикации (контракты) управляют взаимодействием элемента с его ок ружением.

Основная метафора данного подхода заимствована из бизнеса — компоненты программной системы взаимодействуют друг с другом на основе взаимных обязательств (obligations) и выгод (benefits). Кон тракт — это набор утверждений (assertions), которые описывают функциональность каждого конкретного метода. Утверждения могут быть трех видов: предусловия, постусловия и инварианты.

Если компонента-поставщик предоставляет окружению (клиен там) некоторую функциональность, она может сформулировать требо вание в виде предусловия (precondition), истинность которого необхо дима для ее использования. Предусловие определяет выгоду для по ставщика и обязательства для клиентов. Компонента-поставщик, в свою очередь, может гарантировать выполнение некоторого действия с помощью постусловия (postcondition), которое определяет обязатель ство для нее и выгоду для клиентских компонент. Инвариант (invariant) класса — это утверждение, выражающее общие согласован ные ограничения, применимые к каждому экземпляру класса (значени ям его полей). Инвариант применяется к классу в целом и этим отли чается от предусловий и постусловий, характеризующих отдельные модули (методы класса).

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

Постусловие определяет состояние, завершающее выполнение метода. Постусловие гарантирует, что выполнение программы приво дит к состоянию с заданными свойствами, в предположении, что про грамма была запущена в состоянии, удовлетворяющем предусловию.

Инвариант — это множество утверждений, которым удовлетворя ет каждый экземпляр класса во все «стабильные» моментыы времени:

на момент создания экземпляра, т. е. сразу после выполнения конструктора;

перед вызовом и после вызова любого метода данного класса.

Вполне возможно, что некоторый метод в процессе своей работы раз рушает инвариант, но, завершая работу, он обязан его восстановить.

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

Таким образом, контракт является спецификацией класса, которая точно описывает, какие сервисы предоставляет класс.

8.2. Применение контрактного проектирования на основе АОП и системы Aspect.NET Встроенная поддержка технологии контрактного проектирования присутствует далеко не во всех языках программирования. В то же время, при попытке соблюдать контракты обычными средствами в языках без прямой поддержки возникают следующие проблемы:

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

Код проверки утверждений контракта перемешивается с ос новным кодом компонент. Это ухудшает познаваемость кода и, как следствие, повышает вероятность внесения ошибок.

Код проверки утверждений контрактов рассредоточивается по всей системе. Если возникает потребность изменить какое либо из условий, приходится производить изменения во всех модулях, на которые оно распространяется.

Реализация проверки утверждений является достаточно гро моздкой. При удалении, вставке и модификации проверок достаточно сложной задачей является поддержание всей сис темы в целом в согласованном состоянии.

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

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

Как уже известно читателям, одним из существующих решений подобных проблем является аспектно-ориентированное программиро вание (АОП).

8.3. Принципы спецификации контрактов и их реализация в виде аспектов системы Aspect.NET В качестве примера внедрения контрактных спецификаций в про граммы, написанные на языке C#, рассмотрим класс Stack, модели рующий работу со стеком — структурой с дисциплиной доступа к элементам по принципу Last In, First Out (LIFO) — обслуживание в порядке, обратном порядку поступления (буквально: последним при шел — первым обслужили).

public partial class Stack { private int capacity;

//max number of elements private int count;

//actual number of elements private int[] representationArray;

// stack representation public bool IsEmpty() { return (count == 0);

} public bool IsFull() { return (count == capacity);

} public void Push(int element) // Add element on top {... } public int Pop() // Get the top element {... } } // Stack Прежде всего, следует выделить семантические свойства класса, не зависящие от специфики реализации, и на их основе разработать спецификацию. Для класса Stack такими свойствами являются:

1. Метод Push не может быть вызван, если стек заполнен.

2. Метод Pop не может быть применен к пустому стеку.

3. После завершения работы метода Push стек не может быть пуст;

на его вершине находится только что добавленный элемент;

чис ло элементов стека увеличилось на 1.

4. После завершения работы метода Pop стек не может быть пере полнен;

метод возвращает элемент с вершины стека;

число элементов стека уменьшилось на 1.

Следует обратить особое внимание на методы IsEmpty и IsFull. В терминах класса Stack постусловие для этих методов должно прове рять, является ли возвращаемое значение результатом сравнения count == 0 (или count == capacity). Внутри метода именно это значение и было передано оператору return. Возникает вопрос: не является ли постусловие избыточным?

Мейер дает однозначный ответ на этот вопрос: между двумя ука занными конструкциями существенная разница. Присваивание — это команда (инструкция), в результате выполнения которой изменяется состояние. Утверждение же лишь специфицирует свойство ожидаемо го заключительного состояния. Инструкция является частью реализа ции, утверждение — элементом спецификации. Клиенты модуля инте ресуются утверждениями, а не реализациями. При переходе к более сложным примерам различие между спецификацией и реализацией будет возрастать.

Таким образом, определен контракт для класса Stack. Следова тельно, разработчик класса при реализации его методов может пред полагать, что все ограничения, заданные предусловием, выполняются;

поэтому нет необходимости проверять их в теле метода. Так, напри мер, для метода Push можно предполагать, что стек не переполнен, поскольку это предусмотрено предусловием. В дополнительной про верке не только нет необходимости, но это и недопустимо с точки зре ния методологии контрактного проектирования. Это правило можно сформулировать следующим образом: ни при каких обстоятельствах в теле программы не должно проверяться ее предусловие (non redundancy principle).

С этой точки зрения теория контрактного проектирования не сколько противоречит принципам более раннего подхода — защитно го программирования (defensive programming) по Майерсу [18]. Идея последнего в том, что для обеспечения надежности каждая программа должна защищать себя настолько, насколько это возможно — лучше больше проверок, чем их недостаточное число;

дополнительная про верка в худшем случае будет бесполезной. Проектирование по кон тракту утверждает обратное — избыточные проверки могут нанести вред.

Конечно, если ограничить видение проблемы только одной про граммой, кажется, что включение дополнительной проверки делает программу более устойчивой. Но система и предметная область не ограничиваются одной программой — они содержит множество про грамм в множестве классов. Для получения надежной системы необ ходимо перейти к макроскопическому видению проблемы, обобщаю щему архитектуру.

С этой глобальной точки зрения простота становится критиче ским фактором. В своей монографии Мейер, исследуя базисные меха низмы надежности, делает вывод: «самый большой враг надежности (и качества программного обеспечения в целом) — это сложность. Соз давая наши структуры настолько простыми, сколь это возможно, мы достигаем необходимого (но не достаточного) условия, гарантирую щего надежность. Также необходим (но не достаточен) постоянный акцент на создание элегантного и понятного программного обеспече ния. Программные тексты не только пишутся, они еще и читаются и переписываются по многу раз. Ясность и простота нотации языковых конструкций — основа любого изощренного подхода к надежности».

Итак, контракт для класса Stack определен, и можно переходить к его реализации в виде аспектного модуля. Определение точек вставки для проверки предусловий методов Push и Pop может выглядеть сле дующим образом:

%before %call *Stack.Push(int) && %args(..) %action public static void CheckPushPrecondition (int element) {... } %before %call *Stack.Pop() %action public static void CheckPopPrecondition() {... } Немного сложнее обстоит дело с постусловиями. В языках со встроенной поддержкой контрактного проектирования в постусловиях доступна специальная конструкция old выражение. Она позволяет получить доступ к значению, которое данное выражение имело на входе программы. Соответственно, если выражению не предшествует old, имеется в виду значение на выходе программы.

Поскольку в нашем распоряжении нет подобной функционально сти, оказывается недостаточным просто написать:

%after %call *Stack.Push(int) && %args(arg[0]) %action public static void CheckPushPostcondition (int element)... } поскольку внутри метода нет никакой возможности получить оба со стояния вызывающего объекта — на входе и на выходе программы.

Однако это алгоритмическое затруднение может быть разрешено следующим образом:

%instead %call *Stack.Push(int) && %args(arg[0]) %action public static void CheckPushPostcondition (int element) { Stack originalStack = (Stack)TargetObject;

originalStack.Push(element);

Stack resultStack = originalStack;

// сравнение originalStack и resultStack } Аналогично, для метода Pop():

%instead %call *Stack.Pop() %action public static void CheckPopPostcondition() { Stack originalStack = (Stack)TargetObject;

originalStack.Pop();

Stack resultStack = originalStack;

// сравнение originalStack и resultStack } В случае, когда результатом выполнения метода является некото рое возвращаемое значение, например, как в методах IsFull и IsEmpty, можно воспользоваться специальной конструкцией RetValue (доступна в версии Aspect.NET 2.2):

%after %call *Stack.IsEmpty() %action public static void CheckIsEmptyPostcondition() { Stack originalStack = (Stack)TargetObject;

if (bool)RetValue != (originalStack.count == 0)) {…} } Соблюдение принципов контрактного проектирования является основой для решения многих проблем, критических для объектно ориентированного подхода: на какие понятия опираться на стадии ана лиза, как специфицировать компоненты, чем руководствоваться при выполнении тестирования. Перечисленные преимущества достигаются за счет явного и четкого разделения обязанностей между классами и их пользователями.

Это, в первую очередь, приводит к систематизации и упрощению проектирования. Процесс становится для участников более прозрач ным и понятным.

Значительно улучшается ясность программных текстов, что обу славливает лучшее понимание кода как самими разработчиками, так и другими участниками технологического процесса: аналитиками, соз дателями тестов по методу белого ящика. Результат — более полные и эффективные процедуры тестирования и отладки.

Контракты существенно упрощают процесс создания программ ной документации, так как спецификации класса, описывающие свой ства его атрибутов и методов, сами по себе являются хорошей доку ментацией.

Становится более приемлемым повторное использование клас са — как за счет улучшенной документации, обеспечивающей кор ректное использование класса, так и за счет возможности проверки корректности применения класса в новых условиях.

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

Более подробно контрактные спецификации стека описаны в ра боте [1]. Там же даны и другие содержательные примеры применения АОП для контрактного проектирования.

В настоящее время на базе описанных идей разрабатывается биб лиотека аспектов поддержки контрактного проектирования Aspect4DBC.

Вопросы к главе 1. Что такое контрактное проектирование?

2. Что такое контракт метода и в чем смысл его двух основных час тей — предусловия (require) и постусловия (ensure)?

3. Какую роль в контракте класса играет его инвариант? Приведите кон кретный пример инварианта класса.

4. Почему АОП удобно для реализации проверок выполнимости кон трактных спецификаций?

5. Сформулируйте контрактные спецификации для стека.

Упражнения к главе 1. Сформулируйте контрактные спецификации для понятия списка с операциями Cons(x, l) — добавить элемент x в начало списка l;

Car(l) — первый элемент (голова) списка, Cdr(l) — список без перво го элемента (хвост);

Nil — пустой список.

2. Реализуйте проверки предусловий и постусловий контракта списка в виде аспекта в системе Aspect.NET.

Глава МНОГОЯЗЫКОВОЕ АОП 9.1. Роль и принципы реализации многоязыкового АОП В силу ряда исторических причин, большинство инструментов ас пектно-ориентированного программирования, начиная с наиболее из вестной, ставшей классической, системы AspectJ, реализованы как расширения какого-либо определенного языка программирования (как правило — языка Java) концепциями и конструкциями для поддержки АОП.

Преимущество подобного подхода в том, что он дает возможность пользователям привычного, постоянно используемого языка, оставаясь в рамках его хорошо освоенных понятий и обозначений, эксперимен тировать в изучении и использовании новой технологии АОП.

Недостаток «одноязыкового» подхода к АОП — в его ограничен ности: средства поддержки АОП доступны лишь как «продолжение»

конкретного языка и основанной на нем системы программирования (специфических, реализованных специально для данного АОП-расши рения, компилятора, редактора, отладчика, профайлера и т. д.), зависят от них и не могут быть использованы при программировании на дру гом языке, что неудобно, так как для многих больших программных систем принято разрабатывать модули, реализующие различного рода функциональности, на различных, наиболее подходящих для этого языках, например — на языках C# и Visual Basic (VB) для платформы.NET.

С методологической точки зрения, одноязыковый подход к АОП приводит также к «концептуальной путанице» (conceptual confusion), так как в одном и том же расширенном языке оказываются доступны ми и комбинируются понятия совершенно различной природы и раз ного уровня общности — например, классы (как основное средство ООП) и аспекты (как новая разновидность модулей для описания сквозной функциональности). Это подчас вызывает серьезные допол нительные проблемы в понимании и использовании АОП даже опыт ными программистами и существенно затрудняет его изучение и рас пространение.



Pages:     | 1 || 3 |
 





 
© 2013 www.libed.ru - «Бесплатная библиотека научно-практических конференций»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.