Статьи

Расширенные возможности сценариев в Activiti: внедрение пользовательских настроек

Задача сценариев, вероятно, является одним из «старейших» классов в кодовой базе Activiti , но я думаю, что многие до сих пор не используют его. (Предполагаемые?) Недостатки — это, конечно, производительность (интерпретация или компиляция) и меньшая поддержка с точки зрения IDE.

Тем не менее, преимущества (IMO) перевешивают это:

  • Сценарии определяются в самом процессе xml. Больше не нужно беспокоиться о версии и необходимости манипулирования библиотеками на пути к классам.
  • То, что мы видели в прошлом, — это то, что менее технические специалисты осмеливаются пробовать сценарии. Но никогда не Java.

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

Позвольте мне провести вас через такой пример. Мне нравится называть это «внедрение нестандартной конфигурации» в качестве концепции , поскольку она позволяет эффективно добавлять настраиваемую логику во время выполнения, что существенно изменяет выполнение процесса. Если у вас есть кулер, пожалуйста, дайте мне знать.

Весь код можно найти на моей странице Github: https://github.com/jbarrez/activiti-advanced-scripting

удивительная-кода 648x303

Вариант использования

Теперь, что это за вещь, которую я хочу сделать. Ну, я хочу иметь процесс, который при исполнении

  • Добавляет «обработчик события завершения задачи» к каждой пользовательской задаче, которая выполняется
  • Этот обработчик событий должен запускать пользовательское событие для удаленного URL-адреса, где потенциально обработчик событий делает свое дело

Таким образом, в основном, мы хотим запускать пользовательские события для некоторого удаленного URL, когда задача завершена. Хорошим примером использования для этого могут быть отчеты Business Intelligence / сложная обработка событий, например с чем-то вроде Esper .

Screen-Shot-2013-07-23-на-10.03.112

Первая версия

Первый вариант этой функции можно найти по адресу 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 url = new URL('http://localhost:8182/echo');";
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, вы увидите что-то вроде этого:

Screen-Shot-2013-07-23-в-09.53.00

Но мы можем сделать лучше

Но мы можем сделать лучше. Проверьте следующий код.

1
2
3
4
5
6
7
8
var handler = new ExecuteScriptOnTaskCompleteBpmnParseHandler("JavaScript");
handler.setUserTaskCompleteScript("http://localhost:8182/scripts/task-complete.js");
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 на тестовом сервере.

Screen-Shot-2013-07-23-на-09.50.362-1024x171

В тесте вы можете видеть, что мы должны выполнить асинхронное задание, чтобы увидеть результаты теста.

Предостережение

Небольшое предостережение: при перезагрузке процессорного механизма конфигурация будет перезагружена из файла конфигурации. Следовательно, процесс сверху, который внедряет пользовательскую логику, не добавляется. Однако это легко сделать с помощью реализации ProcessEngineLifeCycleListener, которая выполняет определение процесса определенной категории после загрузки ядра. Если вы, например, задаете все эти процессы «config-process» как категорию, они могут легко выполняться при загрузке.

Вывод

Сценарии в процессах BPMN 2.0 — очень мощная функция. Это позволяет вам изменить процесс выполнения всего двигателя в несколько строк. Конечно, весь приведенный выше код может быть выполнен с помощью Java. Но в приведенных выше примерах используется не что иное, как стандартный BPMN 2.0 и механизм javascript, который поставляется вместе с каждой установкой JDK.

Спасибо за чтение. Удачного кодирования!