 | Уровень сложности: средний Джеффри Бирс, инженер-программист, IBM Джеймс Кэри, старший инженер-программист, IBM
08.11.2007 Данная серия статей содержит обзор возможностей Business State Machine (машины бизнес-состояний) и рассказывает об использовании в качестве альтернативы процессов Business Process Execution Language (BPEL-процессов). Эта серия посвящена созданию торгового автомата (vending machine), и с каждой статьей вы сможете его совершенствовать, используя новые возможности BSM.
Введение
Компонент Business State Machine (BSM), входящий в состав, ПО IBM® WebSphere® Process Server предоставляет возможность определения и работы с "бизнес-сущностями" (business "nouns"). Такими сущностями являются, например, "заказы (orders)" или "рейсы (trips)", у которых есть жизненный цикл, управляющий их поведением. Например, операция отмены рейса не будет поддерживаться, если рейс уже завершён, и в дальнейшем потребуется различная обработка для зарезервированного рейса и для не зарезервированного. BSM обеспечивает способ разработки, отладки и мониторинга этих сущностей как машины состояний.
Данная серия статей содержит обзор компонента Business State Machines (машины бизнес-состояний) и его возможностей и рассказывает об использовании BPEL-процессов в качестве альтернативы. Эта серия посвящена созданию торгового автомата (vending machine), который вы сможете совершенствовать в процессе чтения каждой из статей, используя новые возможности BSM. Прочитав первую статью, вы узнали основные возможности BSM, смогли создать базовый торговый автомат и протестировать его. Во второй статье мы заставили свой торговый автомат принимать деньги и протестировали его с помощью компонентного теста. В заключительной статье мы научим автомат отпускать купленные товары, задав автомату определенные условия и таймауты, а также тестировать его при помощи отладчика. Наконец, вы научитесь упрощать машину состояний при помощи композитных состояний.
Что наш торговый автомат может делать на данный момент?
Пока его можно включать и выключать, и он умеет принимать деньги, что и показано на рисунке 1.
Рисунок 1. Торговый автомат, который может включаться, выключаться и принимать деньги
Выдача товаров
Теперь вы уже знаете, как принимать и возвращать деньги, и остается научить автомат выдавать сами купленные товары. Для этого можно было бы просто добавить операцию покупки и связать ее с переходом от состояния Collecting (прием) к состоянию Running (обработка). Вместо этого давайте добавим новое состояние, Ready To Dispense (готов к выдаче). Это состояние будет запущено, если на счету есть достаточно денег для покупки самого дешевого из заряженных товаров, как показано на рисунке 2.
Рисунок 2. Торговый автомат с добавленным состоянием Ready To Dispense (готов к выдаче)
Переход от состояния Collecting к состоянию Ready To Dispense - автоматический. Это означает, что он не вызывается операцией или таймаутом (об этом мы расскажем позже). Чтобы такой переход не произошел сразу же, нужно добавить условие, обозначенное голубой ромбовидной иконкой. Это условие может быть или фрагментом кода Java™ или вызовом компонента, который возвращает булево значение. Если возвращенное значение - истинно, выполняется переход. В данном примере условие - это фрагмент кода Java, который удостоверяет, что параметр totalDeposited (всего внесено на счет) - больше 75 центов. Если это так, происходит переход к состоянию Ready To Dispense. В противном случае сохраняется состояние Collecting, пока не будет внесено достаточно денег, чтобы купить единицу самого дешевого товара (75 центов). Добавление этого автоматического перехода важно в том отношении, что самопереход к счету в состоянии Collecting - внешний переход. Автоматические переходы из какого-либо состояния подлежат проверке только при переходе в это состояние. Если самопереход - внутренний, перехода в это состояние не происходит, поэтому автоматические переходы не проверяются.
Переход, связанный с операцией внесения на счет в состоянии Ready To Dispense - идентичен переходу в состояние Collecting. Этот самопереход разрешает добавлять деньги до суммы, которая будет достаточна для покупки выбранного товара. Этот самопереход тоже должен быть внешним, но по другой причине: для того, чтобы каждый раз при добавлении денег запускалось действие Light Options.
Когда запущено состояние Ready To Dispense, можно выполнять операцию покупки. Переход, связанный с этой операцией, проверяет соответствие условию: (Enough? (достаточно ли?)), и будет осуществлен при утвердительном ответе. Это условие проверяет, чтобы сумма totalDeposited (всего внесено) была не меньше purchaseAmount (суммы покупки). В данном примере сумма покупки передается в операцию покупки, но в реальном приложении будет передаваться идентификатор приобретаемого товара, а значение суммы будет получено в результате поиска. В нашем примере передаваемое значение будет доступно условию точно также, как при операции перехода. Это значение находится в локальной переменной, которая называется purchase_Input_purchaseAmount. Но обратите внимание на то, что выходные переменные недоступны из Java-фрагментов, содержащих условия, поскольку возможно, что проверяться будут многие условия, прежде чем обнаружится условие, возвращающее значение true.
Вызов другого компонента
Если есть возможность использовать фрагмент кода Java, значит, вместо него можно воспользоваться вызовом и другого сервиса. Для торгового автомата временно изменим условие Enough for Cheapest? (достаточно ли для самой дешевой покупки?) для вызова другого компонента. До этого следует определить интерфейс (см. рисунок 3), потому что вызываемый компонент должен иметь определение.
Рисунок 3. Интерфейс ProductInfo, проверяющий достаточность внесенной суммы
Поскольку этот вызов осуществляется при помощи условия, возвращаемое значение должно иметь тип Boolean. При помощи этого значения нужно определить, следует ли осуществлять этот переход (true) или нет (false).
Далее необходимо добавить его в References (Ссылки), которыми пользуется ваш торговый автомат. Сделайте это в разделе редактора References, нажимая иконку add references (плюс), как показано на рисунке 4.
Рисунок 4. Ссылка для интерфейса ProductInfo
Этой ссылкой теперь можно пользоваться для указания того, что следует вызвать операцию в интерфейсе, связанном с данной ссылкой. Для этого надо выбрать условие или действие, которое будет осуществлять вызов, затем выбрать соответствующие ссылку и операцию, как показано на рисунке 5.
Рисунок 5. Вызов свойств для условия
Потом ввод и вывод для этой операции можно связать с переменными в машине состояний. Для этого надо выбрать Input или Output переключателем Parameter (в данном примере выбран Input). Выбираем переменную и соответствующую ей операцию, нажимаем Set > и повторяем это для каждого параметра. Когда величины заданы, они показаны с разделением символом ">". В нашем примере параметр ввода totalDeposited для операции enoughForCheapest задается на основании локальной переменной totalDeposited. Обратите внимание на то, что в этом примере условие связано с автоматическим переходом, поэтому операции для задания параметров нет. Также, поскольку переход задается для условия, булево число на выводе обрабатывается автоматически, поэтому в этом случае положение переключателя Output недоступно.
Программным путем можно вызывать и другие компоненты из фрагмента кода Java. Это становится необходимым, когда до вызова или после него нужно произвести расчеты.
Поскольку нас прежде всего интересуют BSM, мы удалили это изменение и восстановили исходный фрагмент кода Java.
Поддержка BSM реакции на превышение времени ожидания
Нам вовсе не нужно, чтобы гасла подсветка товаров, которые можно приобрести, поэтому при превышении времени, отведенного на покупку, операция должна прекратиться. Это можно сделать при помощи задания таймаутов, которые поддерживает BSM. Есть два типа таймаутов: по длительности и по истечению. Длительность - это фиксированное время, например, 30 минут, а истечение - это точка во времени, например, в полночь 12 декабря 2007 г. В нашем примере на рисунке 6 мы задали превышение в 10 минут.
Рисунок 6. Установка таймаута для торгового автомата
Мы добавили два таймаута по 10 минут каждый, один из состояния Collecting, а второй - из состояния Ready To Dispense. Эти связанные с таймаутами переходы не нуждаются в условиях, потому что мы задаем для них именно 10 минут. Обратите внимание на то, что вообще добавлять условия следует с осторожностью, поскольку таймер обнуляется только тогда, когда вводят состояние, а это означает, что таймаут, имеющий условие, проверяется только один раз. Еще одно важное обстоятельство связано с самопереходами. Если наш самопереход, связанный с вкладом, является внутренним, то наш таймер, установленный на 10 минут, не будет срабатывать каждый раз при добавлении денег (поскольку из это состояния не выходят для остановки таймера и последующего его повторного запуска). С другой стороны, если самопереход - внешний, каждый случай использования операции deposit будет перезапускать таймер. Этот самопереход - внешний, и это обеспечивает нужные нам действия, то есть этот переход произойдет после 10 минут бездействия.
Оба перехода, связанные с таймаутами, ведут к состоянию Running, а это значит, что totalDeposited обнулится в результате ввода данных.
Отладка машины состояний
Машину состояний можно отладить, задавая точки прерывания (breakpoints) для выбранных состояний и запуская сервер в режиме отладки. Чтобы задать точку прерывания, щелкните правой кнопкой мыши по значку состояния и выберите Debug и Add Entry Breakpoint:
Рисунок 7. Открытие отладчика машины состояний
Когда задана точка прерывания, в правом верхнем углу окна состояния появится голубая точка (как видно в состоянии Collecting). Теперь протестируйте машину состояний при помощи Business Process Choreographer Explorer или компонентного теста (мы описали их, соответственно, в 1 и 2 частях данной серии). Обратите внимание на то, что если точка прерывания задана для состояния, на которое осуществляется переход с исходного состояния (в данном случае Running), точка прерывания не сработает при первом переходе. Эта проблема будет исправлена в пакете, который скоро выйдет. Любые другие переходы в это состояние заставят точку прерывания сработать. Для нашего примера это означает, что первая точка прерывания, которая сработает, находится в состоянии Collecting. Это произойдет, когда мы вначале вызовем powerOn, а затем добавим 25 центов через deposit.
Когда введено состояние Collecting, Integration Developer переключается в перспективу отладки Debug, в которой отображаются представления Debug, Breakpoints и Variables, что показано на рисунке 8.
Рисунок 8. Отладчик с заданными точками прерывания
Представление Debug показывает потоки и позволяет управлять процессом выполнения программы. Например, нажатие на символ воспроизведения (playback) возобновляет выполнение программы. Что касается торгового автомата, интересно представление Variables (см. рисунок 9).
Рисунок 9. Переменные машины состояний в отладчике
В этом представлении отображаются переменные, которые были заявлены в составе машины состояний. Для данного автомата это totalDeposited и serialNumber.
Еще мы видим здесь несколько внутренних переменных. Эти внутренние переменные для ввода и вывода операций являются частью интерфейса API для данного Java-фрагмента и едва ли будут изменяться. Они заданы по образцу <operation>_Input_<part> или <operation>_Output_<part>. Переменные ввода, например, deposit_Input_depositAmount, содержат параметры предыдущего вызова данной операции. Соответственно, параметры вывода будут содержать последний результат вывода. Обратите внимание на то, что до начала нового использования переменных эти параметры не отображаются. Например, операция coinReturn не вызывалась в нашем тесте, поэтому переменная coinReturn_Input_serialNumber не показана.
Остальные внутренние переменные используются для внутреннего управления машиной состояний и не считаются открытыми (public). Хотя в дальнейшем, возможно, они будут изменяться, сейчас интерес представляют ActState, содержащая внутренне имя текущего состояния, и ActTrans, - в ней содержится имя последнего осуществленного перехода.
Недостатком использования отладчика является то, что сейчас он не приостанавливает таймеры. То есть, если вы остановились в точке прерывания на более долгое время, чем задано одним из задействованных таймеров, этот таймер сработает сразу, как только вы возобновите работу. В нашем примере это значит, что если остановились в точке прерывания на время больше 10 минут, то при возобновлении работы вас ожидает немедленный переход в состояние Running, и вы попадете во введенную точку прерывания, которую задали для этого состояния (поскольку она не является начальным переходом (initial transition) в это состояние).
Упрощение машины состояний
BSM имеет еще одно состояние, которое называют композитным. Его используют при деструктуризации машины состояний в логическую иерархию машин состояний. При этом на самом деле новая машина состояний не создается. В нашем примере машину состояний можно было бы упростить при помощи композитного состояния, показанного на рисунке 10.
Рисунок 10. Торговый автомат после упрощения при помощи композитного состояния
Основное упрощение, происходящее здесь, следующее: 10-минутный переход таймера уже незачем передавать из состояния Collecting в Running, а Ready To Dispense - в состояние Running, - все делается одним переходом из композитного состояния. Другими словами, переход, произведенный из композитного состояния, это переход, который можно осуществить из любого состояния в композитном состоянии. Это очень полезно для таких объектов, как в нашем примере таймаут, а также для ситуаций, когда от каждого состояния требуется поддержка отмены. Фактически, в ранее созданных нами машинах состояний была ошибка. Операция coinReturn должна была срабатывать, когда торговый автомат находился в состоянии Ready To Dispense, но поскольку у нас не было явного перехода, подобного переходу от состояния Collecting, операция coinReturn была бы проигнорирована. Когда мы переместили переход для операции coinReturn в машину композитных состояний, проблема решилась, так как теперь операция применима к обоим состояниям в машине композитных состояний. Иначе говоря, создавая переход для coinReturn из CompositeState1 в Running, мы добиваемся того же результата, как если бы мы создали такой же переход из Collecting в Running, а Ready To Dispense в состояние Running. Переход от машины композитных состояний, вызванный таймером или операцией, называет переходом по исключению (exception transition).
Еще один тип перехода из машины композитных состояний - переход по умолчанию. Это автоматический переход, и он происходит, когда композитное состояние достигает своего конечного состояния. Переход по умолчанию возможен только один, и для него не допускается блокировка (guard). Если конечного состояния в машине композитных состояний не имеется (например, в случае, когда требуются только переходы по исключению) то переход по умолчанию не требуется указывать.
Хотя в нашем примере этого не было, можно назвать еще один способ остановить машину композитных состояний - при помощи команды terminate state. Действие terminate state (иконка "X") такое же, как если бы было достигнуто конечное состояние в ближайшей к нам машине состояний. Другими словами, машина состояний останавливается и моментально удаляется. Командой terminate state следует пользоваться с максимальной осторожностью, и как правило, только в экстремальных ситуациях
Композитное состояние также может стать объектом перехода. В нашем примере таковым является переход от состояния Running к операции deposit. Переход, который направлен на машину композитных состояний, заставляет это состояние вернуться в свое исходное состояние (в нашем случае это InitialState2) и немедленно перейти в свое первое "настоящее" состояние. Точно так же, как на главной машине состояний, возможен только один переход из исходного состояния композитного состояния; однако, поскольку этот переход происходит под управлением переходом в машину композитных состояний, он должен быть автоматическим (то есть, с ним не может быть связан таймаут или операция). Количество переходов в композитное состояние может быть любым, и они поддерживают самопереходы. Самопереходы - всегда внешние, и композитное состояние будет закрыто и сделан повторный вход в него, чтобы оно оказалось в своем исходном состоянии.
В нашем примере упрощение при помощи композитного состояния действительно изменило поведение торгового автомата. Самопереход в состояниях Collecting и Ready To Dispense к операции deposit (внесение денег) раньше использовался для перезагрузки таймера. Поскольку время было установлено для каждого состояния и самопереход был внешним, таймер перезагружался при каждом внесении денег. Теперь, когда таймер связан с композитным состоянием CompositeState1, поскольку самопереход осуществляется внутри композитного состояния, перезагрузка таймера не происходит. Одним из способов осуществить это будет передать самопереход композитному состоянию, как показано на рисунке 11.
Рисунок 11. Композитное состояние с добавленным самопереходом
Теперь этот самопереход (помните, что все самопереходы в композитных состояниях - внешние) будет заставлять таймер, установленный на 10 минут, перезагружаться каждый раз, когда вносят деньги. Недостатком этого способа является то, что пополнение счета также вызовет перезагрузку машины композитных состояний. Другими словами, если машина состояний находилась в состоянии Ready To Dispense и внесли еще 20 центов, будет выполнено действие внесения (Update Total), а потом машина состояний перейдет в InitialState2, автоматически перейдет в состояние Collecting, проверит условия (результат окажется положительным, потому что мы уже раньше были в состоянии Ready To Dispense), а потом сделает переход к состоянию Ready To Dispense. В этом случае машина состояний поведет себя как ожидалось. Но в ситуациях, когда для маневрирования с переходами используется много операций, это может оказаться нежелательным.
Альтернативный способ сохранить перезагрузки таймера - оставить переходы таймера внутри машины композитных состояний. Переходы будут двигаться к конечному состоянию внутри машины композитных состояний, потом произойдет выход из этой машины композитных состояний и переход по умолчанию (автоматический переход от композитного состояния в простое состояние Running). Даже если это произошло, полезно было бы использовать композитное состояние, чтобы не потребовалось передавать операцию coinReturn в композитное состояние.
Еще одна причина пользоваться машинами композитных состояний - их способ обработки операций. Например, представим себе переход внутри машины композитных состояний, вызванный операцией coinReturn см. рисунок 12.
Рисунок 12. К состоянию внутри композитного добавлена вторая операция coinReturn
В этом случае, если бы произошла ошибка при операции coinReturn в состоянииCollecting, произошел бы второй самопереход; однако в состоянии Ready To Dispense произошел бы переход к состоянию Running. Если бы с этим новым переходом была связана блокировка, произошла бы ее проверка, и в случае подтверждения произошел бы переход в состояние Running. Иначе говоря, вначале происходит проверка перехода в самом этом состоянии, а если таких переходов не было, тогда проверяются переходы в композитном состоянии, которое содержит данное состояние.
Состояния внутри композитного тоже могут быть композитными. Их наличие не меняет функциональности, но с ними некоторые очень сложные машины состояний могут стать понятнее.
Заключение
Хотя можно было и дальше работать над торговым автоматом, превращая его в полноценное приложение, мы уже достаточно с ним поработали и теперь можем использовать возможности, заложенные в машине бизнес-состояний Process Server. Вы ознакомились с различными типами состояний (простое, исходное, конечное, композитное и завершение работы), разными видами переходов (операция, автоматический и с превышением времени), условиями, корреляциями и некоторыми специфическими моментами самопереходов. Вы также научились тестировать машины состояний при помощи Business Process Choreographer Explorer, компонентного теста и отладчика. Это должно послужить вам прочной основой для разработки машин состояний для конкретных требований.
Ресурсы Научиться
Получить продукты и технологии
-
Пробное ПО IBM
Создайте свой следующий проект с помощью пробных версий программ от компании IBM, которые можно скачать с сайта developerWorks (EN).
Обсудить
Об авторах  | |  | Джеффри Бирс (Geoffrey Beers) - инженер-программист в отделении IBM в Рочестере, Миннесота. Он работает в лаборатории Rochester Bringup и команде SWAT для WebSphere Process Server. С Джеффри можно связаться по адресу beersga@yahoo.com. |
 | |  | Джеймс Кэри (James Carey) - в настоящее время старший инженер-программист в IBM System & Technology Group. Ранее Джеймс был членом команды по архитектуре WebSphere Process Server, где занимался машинами бизнес-состояний. |
Выскажите мнение об этой странице
|  |