Задача сценариев, вероятно, является одним из «старейших» классов в кодовой базе Activiti , но я думаю, что многие до сих пор не используют его. (Предполагаемые?) Недостатки — это, конечно, производительность (интерпретация или компиляция) и меньшая поддержка с точки зрения IDE.
Тем не менее, преимущества (IMO) перевешивают это:
- Сценарии определяются в самом процессе xml. Больше не нужно беспокоиться о версии и необходимости манипулирования библиотеками на пути к классам.
- То, что мы видели в прошлом, — это то, что менее технические специалисты осмеливаются пробовать сценарии. Но никогда не Java.
Как бы то ни было, мало кто знает или осознал, что вы можете делать действительно потрясающие и продвинутые вещи в сценариях в Activiti. Поскольку такой сценарий выполняется внутри механизма процесса, у вас есть доступ ко всему, на что способен механизм. Да … все … что делает его очень мощным, но также (потенциально) опасным (если вы не знаете, что делаете).
Позвольте мне провести вас через такой пример. Мне нравится называть это «внедрение нестандартной конфигурации» в качестве концепции , поскольку она позволяет эффективно добавлять настраиваемую логику во время выполнения, что существенно изменяет выполнение процесса. Если у вас есть кулер, пожалуйста, дайте мне знать.
Весь код можно найти на моей странице Github: https://github.com/jbarrez/activiti-advanced-scripting
Вариант использования
Теперь, что это за вещь, которую я хочу сделать. Ну, я хочу иметь процесс, который при исполнении
- Добавляет «обработчик события завершения задачи» к каждой пользовательской задаче, которая выполняется
- Этот обработчик событий должен запускать пользовательское событие для удаленного URL-адреса, где потенциально обработчик событий делает свое дело
Таким образом, в основном, мы хотим запускать пользовательские события для некоторого удаленного URL, когда задача завершена. Хорошим примером использования для этого могут быть отчеты Business Intelligence / сложная обработка событий, например с чем-то вроде Esper .
Первая версия
Первый вариант этой функции можно найти по адресу https://github.com/jbarrez/activit-advanced-scripting/blob/master/src/test/resources/org/activiti/test/my-process.bpmn20.xml . Когда этот процесс выполняется, происходит следующее:
1
2
|
var config = Context.getProcessEngineConfiguration(); var bpmnParser = config.getBpmnParser(); |
Мы просто выбираем текущий экземпляр ProcessEngineConfiguration. Мы извлекаем экземпляр BpmnParser из этой конфигурации, так как мы хотим изменить общий анализ пользовательских задач для всего движка.
Далее мы создаем скрипт:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
var script = "" ; script = script + "importPackage(java.net);" ; script = script + "importPackage(java.io);" ; script = script + "var connection = url.openConnection();" ; script = script + "connection.setRequestMethod('POST');" ; script = script + "connection.setDoOutput(true);" ; script = script + "var outputStream = new BufferedOutputStream(connection.getOutputStream());" ; script = script + "outputStream.write(new java.lang.String(\"{'eventType':'task-complete'}\").bytes);" ; script = script + "outputStream.flush();" ; script = script + "connection.connect();" ; script = script + "var respCode = connection.getResponseCode();" ; script = script + "if (respCode != 200) " ; script = script + "println('Response code : ' + respCode);" ; script = script + "outputStream.close();" ; script = script + "connection.disconnect();" ; |
Это, очевидно, не самый эффективный способ сделать это, но он точно показывает детали того, что происходит. Сообщение ‘eventType: task-complete’ отправляется на адрес localhost: 8182 через стандартные классы java.net и java.io.
Сложная часть идет дальше:
1
2
3
4
5
6
|
var handler = new ExecuteScriptOnTaskCompleteBpmnParseHandler( "JavaScript" ); handler.setUserTaskCompleteScript(script); bpmnParser.getBpmnParserHandlers().addHandler(handler); // reset the deployment cache such that the new listener gets picked up on a new redeploy config.getProcessDefinitionCache().clear(); |
Здесь мы добавляем класс BpmnParseHandler в конфигурацию движка. Обработчик синтаксического анализа добавит выполнение сценария, определенного выше, к каждому получению «события завершения задачи», отправленного механизмом. Этот обработчик разбора запускается каждый раз, когда анализируется пользовательская задача, что эффективно добавляет нашу функцию «отправить событие в удаленный сервис» к каждой пользовательской задаче, которая сейчас происходит в вашей среде Activiti!
Существует модульный тест, чтобы увидеть, как это работает: https://github.com/jbarrez/activiti-advanced-scripting/blob/master/src/test/java/org/activiti/test/ExecuteScriptInProcessTest.java . В тесте мы установили очень простой «эхо-сервис», который просто распечатывает всякий раз, когда такое событие получено. Если вы запустите его в своей IDE, вы увидите что-то вроде этого:
Но мы можем сделать лучше
Но мы можем сделать лучше. Проверьте следующий код.
1
2
3
4
5
6
7
8
|
var handler = new ExecuteScriptOnTaskCompleteBpmnParseHandler( "JavaScript" ); handler.setExecuteScriptInJob( true ); bpmnParser.getBpmnParserHandlers().addHandler(handler); // Update the configuration to use the correct job handler var jobHandler = new ExecuteScriptJobHandler(); config.getJobHandlers().put(jobHandler.type,jobHandler); |
Этот код делает то же, что и в предыдущем разделе, т.е. присоединение слушателя «завершенных» событий к каждой пользовательской задаче. Однако эта реализация:
- Выполняет скрипт асинхронно
- Не определяет сценарий в процессе XML, но он извлекается из удаленного URL
- Обновляет конфигурацию обработчика заданий
Если вы спросите меня, это очень здорово! Таким образом, это означает, что фактическая отправка сообщения удаленной службе не влияет на производительность выполнения вашего экземпляра процесса . Очевидно, что отсюда вы можете сходить с ума и добавлять постоянные очереди и все такое воображение. И, кроме того, скрипт всегда выбирается с удаленного сервера. Если вы хотите обновить выполняемую логику, просто измените возвращаемый скрипт. Это означает, что вы можете влиять на выполнение процесса в режиме RUNTIME, не касаясь реального процесса.
Для этого есть модульный тест по адресу https://github.com/jbarrez/activiti-advanced-scripting/blob/master/src/test/java/org/activiti/test/ExecuteScriptWithJobTest.java
Если вы запустите этот тест, вы увидите следующее. Обратите внимание, что мы размещаем скрипт завершения в виде статического файла с именем task-complete.js на тестовом сервере.
В тесте вы можете видеть, что мы должны выполнить асинхронное задание, чтобы увидеть результаты теста.
Предостережение
Небольшое предостережение: при перезагрузке процессорного механизма конфигурация будет перезагружена из файла конфигурации. Следовательно, процесс сверху, который внедряет пользовательскую логику, не добавляется. Однако это легко сделать с помощью реализации ProcessEngineLifeCycleListener, которая выполняет определение процесса определенной категории после загрузки ядра. Если вы, например, задаете все эти процессы «config-process» как категорию, они могут легко выполняться при загрузке.
Вывод
Сценарии в процессах BPMN 2.0 — очень мощная функция. Это позволяет вам изменить процесс выполнения всего двигателя в несколько строк. Конечно, весь приведенный выше код может быть выполнен с помощью Java. Но в приведенных выше примерах используется не что иное, как стандартный BPMN 2.0 и механизм javascript, который поставляется вместе с каждой установкой JDK.
Спасибо за чтение. Удачного кодирования!