Возможности граф-ориентированного программирования для систем автоматизированного управления процессами на примере jPDL языка

ВОЗМОЖНОСТИ ГРАФ-ОРИЕНТИРОВАННОГО ПРОГРАММИРОВАНИЯ ДЛЯ СИСТЕМ АВТОМАТИЗИРОВАННОГО УПРАВЛЕНИЯ ПРОЦЕССАМИ

НА ПРИМЕРЕ jPDL ЯЗЫКА В.В. Власов

Научный руководитель - д.т.н., профессор Ю.А. Гатчин

В статье рассматриваются базовые принципы и модели граф-ориентированного программирования на примере языка jPDL (java process definition language), описаны основные отличия от объектно-ориентированного подхода, приведен пример классов, диаграмм и описания процесса, а также объясняется логика выполнения процесса на основе движения «маркера выполнения» по траектории графической диаграммы

Введение

Существующие на сегодняшний день объектно-ориентированные языки программирования не позволяют разрабатывать гибкие, легко (или достаточно легко) настраиваемые, а также легко изменяемые приложения (имеется в виду бизнес-логика). А о том, чтобы не профессионал мог изменять ход выполнения программы, еще несколько лет назад не могло быть речи. Это особенно важно в таких областях, как например, управление бизнес-процессами предприятия, в системах автоматизированного управления ресурсами предприятия, рабочими процессами, документооборота. Даже такой огромный шаг в этом направлении, как появление кросс-платформенного языка Java от Sun Microsystems, не решает эту проблему. Это связано не с тем, что языки программирования плохо продуманы и реализованы, а с тем, что модель объектно-ориентированного языка программирования не включает в себя некоторые важные аспекты, которые интуитивно понятны человеку - поддержка сохраняемого состояния ожидания и графическое представление. Поэтому в последнее время появляется большое количество готовых решений, каркасов (frameworks) для упрощения процесса разработки, позволяющих уменьшить время, затрачиваемое разработчиком на изменение приложения в соответствии с постоянно изменяющимися бизнес-требованиями. Таким образом, уменьшается время между выходами новых релизов продуктов.

Граф-ориентированное программирование (GOP, graf oriented programming) сейчас только оформляется как направление в программировании, хотя общие принципы известны достаточно давно. Типичный представитель языка GOP - Java язык описания процессов jPDL[1], который и будет рассмотрен в данной статье. Разработчики этого языка создали для него и движок выполнения процессов (process virtual machine), который позволяет гибко использовать любой процессный язык. Этот язык интересен еще и тем, что он реализует основные принципы граф-ориентированного программирования без привязки к какой-то конкретной реализации. Поэтому он является хорошим примером как теоретического, так и прикладного применения принципов GOP.

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

Языки предметных областей

Любой язык описания процессов может быть отнесен к классу Domain Specific Language (DSL)[2], языков предметных областей. Важным аспектом DSL является наличие определенной грамматики языка. Так, для языка программирования Java базовыми являются понятия класса, метода, поля, конструктора, для правил - условие, резуль-

тат, для GOP - это узел графа, переходы, события (действия). Основная идея DSL заключается в том, что разработчик «думает» в терминах данной грамматики, когда создает компоненты (или программы), используя этот язык. Так, среды разработки (IDE) для каждого языка программирования строятся вокруг определенных терминов (грамматик) этого языка. Конечно же, кроме вербального описания языка, существует его конкретная реализация в виде файла кода программы (текстовый), блок-схемы (графический). Хотя в объектно-ориентированном программировании и существует практика создания блок-схем на этапе проектирования (или других графических представлений логики, например, с использованием нотаций UML), но, тем не менее, в дальнейшем при кодировании (компилировании, выполнении), т.е. непосредственно при написании кода, создания выполняемых компонентов или во время выполнения программы блок-схемы не являются частью выполнения этих процессов. Для Java разработчик пишет текстовый код программы (файл java) - «исходник», который потом компилируется в byte-код, в виде class-файла, который и является выполняемым. Для DSL характерно существование нескольких представлений «кода», или инструкций для выполнения.

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

Соответственно для GOP, представителем которого является jPDL, логично существование двух редакторов - текстового и графического. Пока разработчик рисует графовую модель процесса, генерируется XML-представление этого процесса с использованием соответствующих нотаций. И это не является роскошью - это просто новый шаг вперед в гибкой разработке ПО, когда значительная часть рутинной работы перекладывается на интегрированную среду разработки (IDE). Однако в любой момент разработчик может отредактировать XML-представление. Современные среды разработок (например, свободно распространяемый Eclipse) осуществляют двунаправленную связь между графическим и текстовым представлениями.

Если десять лет назад значительная часть времени работы программиста тратилась на кодирование, то сейчас происходит смена приоритетов в направлении именно DSL языков. Эта смена приоритетов связана также с появлением большого числа готовых решений, каркасов (frameworks) - jPDL для потоков работ (workflow) в java [1], BPEL для оркестрации веб-сервисов (web-service orchestration) [3], SEAM для pageflow [4]. GOP - это общий фундамент для всех этих типов DSL (рис.1).

Рис. 1. Положение граф-ориентированных языков

В объектно-ориентированном программировании (ООП) структурирование программы происходит группированием методов с данными. Аспект-ориентированное программирование (АОП) [5] предлагает путь извлечения взаимопересекающихся соотношений, а такие модели, как внедрение зависимости (Dependency Injection - DI) [6]

и инверсия контроля (Inversion of Control - IoC)[6], могут облегчить разводку (трассировку) объектов графа, однако в GOP используются другие подходы, о которых будет говориться в статье. Более подробные сведения о DSL языках можно получить в [7].

Возможности GOP

Существует достаточно большое количество языков описания процессов, основанных на графовой модели. Однако между ними есть большая разница, в том числе и в области применения: так, BPEL - это язык, основанный на XML, и используется для «оркестрации» сервисов в архитектуре с общей шиной, Enterprise Service Bus (ESB) [3]. Pageflow process language, язык управления навигацией страниц, используется для веб-приложений. Это два примера использования процессных языков в различных областях.

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

GOP как бы дополняет ООП этими двумя возможностями. Зависимость GOP от ООП означает, что все конкретные процессные языки, реализуемые на основе GOP, должны будут разрабатываться в среде определенного ОО языка. Правда, это относится далеко не ко всем языкам. Язык jPDL, например, предоставляет такую возможность за счет Process Virtual Machine (виртуальная машина процессов) [1], в отличие от языка BPEL, который является только языком описания процессов и никак не привязывается к какому-либо языку программирования.

Состояние ожидания

Императивные языки программирования (например, как Java) используются для определения последовательности инструкций, которые должны быть выполнены системой. Это прекрасный язык для описания таких процессов, как механизм «запрос-ответ» на сервере. Система непрерывно выполняет последовательности инструкций, записанные в коде, начиная от получения запроса и до отправки ответа.

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

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

Чтобы описать вышеприведенный сценарий полностью, нужна система, которая позволит работать с состоянием ожидания. В большинстве приложений данные на время ожидания должны быть сохранены, поэтому простого блокирования потоков недостаточно. Опытные Java-программисты могли бы вспомнить методы Object.wait() и Ob-

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

Графическое представление

В некоторых областях разработки ПО можно получить значительный выигрыш во времени и ресурсах, затрачиваемых на процесс разработки, используя графическое представление бизнес-логики. Управление бизнес-процессами (Business Process Management, BPM) - одна из самых очевидных таких областей применения. Коммуникация между бизнес-аналитиком и программистом улучшается, если используется некий общий язык для описания программируемых процессов в виде графического представления.

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

В GOP описание графа - центральный и необходимый компонент, так же как XML файл, который его описывает. С тех пор, как диаграмма является внутренней частью программы, они всегда синхронизованы, и нет необходимости в ручном переводе графических требований в дизайн приложения. Программа строится на основе графовой модели.

Грамматика GOP

GOP-программа строится на основе интерпретации графа в режиме реального времени (при инициализации, и далее все инструкции сохраняются в источник данных datasource, например, базу данных), т.е. во время выполнения программы. В этом подразделе будет показано, как исполнение графа может быть реализовано в среде выполнения ООП. Под «исполнением графа» в данной статье подразумевается выполнение инструкций, относящихся ко всей диаграмме (или части ее), описанной в виде узлов, связей между ними, и действиями, которые могут быть сопоставлены с различными событиями, о которых пойдет речь дальше. «Исполнение графа» может быть представлено как движение маркера (token) по линиям (траектории) выполнения процесса. Для тех, кто знаком с шаблонами проектирования, это может выглядеть, как комбинация Шаблона команд (command pattern) и Шаблона цепочки ответственности (chain of responsibility pattern).

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

Рис. 2. Схема классов Node

Класс Node - это класс, являющийся образом «графа» в программной модели, и имеет метод execute(). Подклассы этого класса переписывают этот метод, чтобы реализовать собственное, специфическое поведение.

Модель исполнения, которая определена для графа, похожа на конечный автомат или диаграмму состояния в UML. В действительности GOP может быть использовано, чтобы реализовать любой тип поведения.

Исполнение графа (маркер, token) представляется в виде класса Execution и имеет ссылку (связь) на текущий узел (Node) (рис. 3).

Рис. 3. Execution класс

Переходы (transitions) могут передавать выполнение от одного узла (источник, source) другому (пункт назначения, destination) с помощью метода take() (рис. 4).

Transition

+take(Execution e):vo¡d

Рис. 4. Transition take() метод

Когда «исполнение» (маркер) «прибывает» к узлу, этот узел запускает на выполнение метод execute() (рис. 5), который также ответственен за продолжение хода выполнения. «Продолжение исполнения» означает, что узел может передать маркер выполнения следующему узлу через исходящий переход.

Node

+execute(Execution е) : void

Рис. 5. Node execute() метод

Если в текущем узле метод execute() не продолжает выполнение процесса (не перемещает маркер выполнения к следующему узлу), то такое поведение соответствует состоянию ожидания. Также, когда создается новый маркер выполнения процесса (процесс инициализируется в некотором стартовом узле), процесс переходит в состояние ожидания, пока не произойдет некоторое событие.

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

Execution

event(String event) : void

Рис. 6. Execution event() метод

И состояние графа (узла), и состояние маркера выполнения процесса могут быть сохранены: например, в реляционной базе данных, посредством ORM или объекты мо-

гут быть сериализованы в файл. В jPDL об этом разработчику беспокоиться не нужно. По умолчанию используется широко распространенная ORM-библиотека библиотеки Hibernate [8].

Процессный язык

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

На рис. 7 показаны 4 базовых типа узлов: start state (узел начала процесса), decision (узел принятия решения), task (узел, создающий задачу) и end state (узел конца выполнения).

Рис. 7. Подклассы базового класса Node, реализующие разные типы узлов

в процессном языке

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

Рис. 8. Пример графовой модели процесса

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

Теперь посмотрим, что произойдет, когда будет получено событие. В данном случае вызывается метод event() объекта Execution. Event(). Метод находит исходящий переход «по умолчанию» и передает Execution через переход Transition, вызывая метод take() и передавая себя в качестве параметра.

Рис. 9. Создание нового маркера выполнения процесса

Маркер перемещается к узлу Decision, и опять вызывается его метод execute(). Предположим, что этот метод производит некоторые вычисления и решает продолжать выполнение по ветви yes. Посылается 'yes'-event маркеру выполнения, и он переходит к узлу 'doubleCheck'. Теперь предположим, что реализация doubleCheck Task узла создает новую задачу и добавляет ее в список задач проверяющего, и до тех пор, пока проверяющий не отправит подтверждение, процесс будет находиться в состоянии ожидания (рис. 10) - процесс добавления к списку в случае jPDL автоматизирован, т.е. в логике приложения указывается ID соответствующего пользователя.

Рис. 10. Маркер выполнения ожидает в узле 'doubleCheck'

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

Действия

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

Для этого существуют так называемые действия (Actions) - это такие же команды с методом execute(). Действия связаны с событиями.

Существует два базовых события, вызываемые Node классом во время выполнения процесса - node-leave (выход из узла) and node-enter (вход в узел). Эти события делают возможным включать любую программную логику в зависимости от ситуации: при входе в узел или при выходе из него. Аналогично дело обстоит с переходами (см. рис. 11).

Скрытое действие

Когда вызывается переход 'yes': вызвать мой Java класс MyStats.update

(amount>5000) : Decision

doubleCheck :

Task

Скрытое действие

Когда процесс входит в узел deliver вызвать мой Java класс Deliveries.start

deliver : Risk

Рис. 11. Действия, которые обычно скрыты в графическом представлении

В листинге 1 показан пример класса Действия:

Листинг 1: Пример кода для Action класса

package com.ifmo;

import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.exe.ExecutionContext; public class MyStats implements ActionHandler {

public void execute(ExecutionContext context) throws Exception {

//некоторые действия, например вызов некоего метода, который выполняет сохранение данных в базу данных, которые пришли с веб-формы

}

}

Одно обязательное условие - класс должен реализовывать интерфейс ActionHandler, а также метод public void execute(ExecutionContext context).

В листинге 2 показан итоговый XML-файл описания процесса с описанием классов, которые будут вызываться автоматически при определенных событиях.

Листинг 2: Пример xml-файла описания процесса

1 <process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="simple">

2 <start-state name="StartSale">

3 <transition name="" to="(amount>5000)"></transition>

4 </start-state>

5 <decision name="(amount>5000)">

6 <transition name="yes" to="doubleCheck">

7 <action name="update" class="com.ifmo.MyStats"></action>

8 </transition>

9 <transition name="no" to="deliver"></transition>

10 </decision>

11 <task-node name="doubleCheck">

12 <transition name="" to="deliver"></transition>

13 </task-node>

14 <task-node name="deliver">

15 <event type="node-enter">

16 <action name="deliverStart"

17 class="com.ifmo.Deliveries"></action>

18 </event>

19 <transition name="" to="end1"></transition> 2 0 </task-node>

21 <end-state name="end1"></end-state>

22 </process-definition>

Верхний process-definition xml-схемы содержит полное описание процесса. Разумеется, в одной xml-схеме такой элемент должен быть единственным. В качестве атрибута name принимается имя процесса (строка 1). Это имя должно быть уникально для всех процессов в системе. Процесс можно найти по его имени с помощью метода findProcessDefinition() класса org.jbpm.db.GraphSession. Узел start-state определяется в строке 2, также имеет имя, определяемое атрибутом name, также может быть только один для одного процесса (как и end-state в строке 21) и соответствует узлу Start State, описанному выше (рис. 8). В строке 3 определяем переход, который не имеет имени, а также связку узла StartState со следующим с помощью атрибута to. В строке 5 определяется узел decision (таких узлов может быть сколько угодно, однако имя каждого узла должно быть уникально). В строках 6 и 9 имеется два выхода transition из этого узла: yes и no (рис. 10). С переходом yes связывается вызов события <action name="update" class = "com.ifmo.MyStats">, где указывается его имя и класс, с которым оно ассоциируется. Во время работы в этой точке будет вызван метод execute() этого класса, как описывалось раннее. В строке 15 событие связано с узлом, а именно с действием type="node-enter", т.е. вход в узел. Аналогично будет вызван метод exe-cute() класса com.ifmo.Deliveries. Еще один тип узла используется в строке 14, task-node, который создает задачу для пользователя и ждет, пока задача не будет завершена. Вся эта работа скрыта от создателя диаграммы. Хорошей практикой является вынесение части данных, используемые в логике, в этот файл с помощью переменных и правил (в данной статье не рассматривается) - например, имени пользователя, которому должна быть передана задача. Таких узлов также может быть сколько угодно. Выполнение процесса завершается, когда маркер выполнения дойдет до узла end-state (строка 21).

Параллельное выполнение

Допустим, разрабатывается процесс оформления покупки на основе GOP для workflow (потока работ). После того, как клиент отправил заказ, некоторые действия по оплате и доставке товара должны быть произведены параллельно. Для этого предусмотрены Fork и Join узлы, которые начинают параллельное выполнение и завершают его, соответственно (рис. 12). Задача может быть решена на основе многопоточного выполнения процессов, и пока обе ветви не будут завершены, дальнейшее выполнение процесса будет невозможно.

Рис. 12. Параллельное выполнение процессов

GOP на основе jPDL не использует возможности для анализа, прогнозирования, автоматического создания схем бизнес-процессов, однако, используя методы порождающих грамматик [9], можно на основе базовой модели jPDL создавать ее расширения самостоятельно, включая интеллектуальные механизмы для поддержки и обслуживания системы управления бизнес-процессами [10].

Достаточно большое количество шаблонов проектирования процессного программирования описано на сайте [11]. Помимо jPDL, существуют и другие реализации GOP, как свободно распространяемые, например, язык YAWL [12], так и для платных продуктов компаний Microsoft, Oracle и др.

Однако, по мнению автора, преимуществом jPDL языка является не только тот факт, что он относится к классу opensource (свободно распространяемых) продуктов, но и то, что он имеет возможность реализовать программную логику - связку с ООП на Java, имеет виртуальную машину исполнения процессов. jPDL, может работать и как настольное приложение, и как серверное приложение, так как является стандартным java архивом (jar) и легко интегрируется с самым распространенным бесплатным сервером приложений уровня предприятия jBoss Application Server[13]. Также существует плагин для IDE Eclipse, который позволяет автоматически генерировать веб-формы, процессы, а также имеет графический редактор.

jPDL, кроме вышеперечисленных возможностей, имеет поддержку асинхронных действий, таймеров, расписаний, вложенных процессов, поддерживает работу с правилами jBoss Drools, транзакциями, автоматической отправкой сообщений email, логиро-ванием, системой безопасности, интеграцию со сторонними базами данных, уже содержащих описание ролей и пользователей системы, и многое другое, что не рассматривалось в данной статье, но описано в документации по jPDL [1].

Заключение

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

К сожалению, формат статьи не позволяет осветить все аспекты программирования на основе графовой модели, однако это и не нужно. Были показаны основные базовые возможности, которые реализуют большинство процессных языков, в том числе jPDL. Как в свое время язык программирования Java стал значительным шагом в объектно-ориентированном программировании, так и GOP позволит достигнуть новых возможностей как для разработчиков ПО, так и для бизнеса.

Литература

1. JBoss.org. Официальный сайт. - Режим доступа: http://www.jboss.org/jbossjbpm/jpdl/

2. Martin Fowler. Официальный сайт. - Режим доступа: http://www.martinfowler.com/bliki/DomainSpecificLanguage.html

3. Биберштейн H., Боуз С., Фиаммант М., Джонс К. Компас в мире сервис-ориентированной архитектуры (SOA). - М.: Кудиц Пресс, 2008. - 228 с.

4. JBoss Seam. Официальный сайт. - Режим доступа: http://www.jboss.com/products/seam

5. G. Kiczales, J. Lamping, A. Mendhekar, Aspect-Oriented Programming // European Conference on Object-Oriented Programming (ECOOP), Finland. Springer-Verlag LNCS 1241. June 1997.

6. Martin Fowler. Официальный сайт. - Режим доступа: http://www.martinfowler.com/articles/injection.html

7. Martin Fowler. Официальный сайт. - Режим доступа: http://www.martinfowler.com/articles/languageWorkbench.html

8. Hibernate. Официальный сайт. - Режим доступа: http://www.hibernate.org/

9. Калянов Г.Н. Моделирование, анализ, реорганизация и автоматизация бизнес-процессов. - М.: Финансы и статистика, 2006. - 238 с.

10. Власов В.В. Автоматизированная интеллектуальная система поддержки распределенной информационной архитектуры предприятия на основе бизнес-процессного управления (BPM). / Материалы Всероссийского форума студентов, аспирантов и молодых ученых «Наука и инновации в технических университетах». - СПб: Изд-во Политехнического университета, 2007. - С. 54

11. Workflow Patterns home page. - Режим доступа: http://www.workflowpatterns.com/

12. YAWL: Yet Another Workflow Language. - Режим доступа: http://_www.yawl-system.com/

13. JBoss Seam. Официальный сайт. - Режим доступа: http://www.jboss.org/jbossas/