Функция, которая была запрошена совсем немного — временные переменные — появилась в бета-версии Activiti v6, которую мы выпустили вчера . В этом посте я покажу вам пример того, как временные переменные могут использоваться, чтобы охватить некоторые сложные варианты использования, которые раньше были (или не оптимальны) невозможны.
До сих пор все переменные в Activiti были постоянными . Это означает, что переменная и значение хранятся в хранилище данных, а исторические данные аудита сохраняются. С другой стороны, временные переменные действуют и ведут себя как обычные переменные, но они не сохраняются. Помимо того, что оно не сохраняется, для переходных переменных характерно следующее:
- временная переменная сохраняется только до следующего «состояния ожидания», когда состояние экземпляра процесса сохраняется в базе данных.
- временная переменная затеняет постоянную переменную с тем же именем.
Более подробную информацию о переходных переменных и API можно найти в документации .
пример
Определение процесса, которое мы будем использовать для демонстрации некоторых битов переходных переменных, показано ниже. Это довольно простой процесс: идея в том, что мы будем запрашивать у пользователя некоторые вещи, такие как ключевое слово и язык, и использовать его для вызова API GitHub. В случае успеха результаты показываются пользователю. Для этого легко написать пользовательский интерфейс (или использовать новые формы в приложении Beta3 angularJS ), но в этом посте мы сосредоточимся только на коде.
BPMN 2.0 xml и код можно найти в этом репозитории Github: https://github.com/jbarrez/transient-vars-example
Давайте пройдемся по процессу вместе. Процесс начинается с предоставления пользователем информации о том, что нужно искать (обычно это делается с помощью стартовой формы).
|
1
2
3
4
5
6
|
repositoryService.createDeployment().addClasspathResource("process.bpmn20.xml").deploy();Map<String, Object> variables = new HashMap<String, Object>();variables.put("keyWord", "workflow");variables.put("language", "java");ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("githubsearch", variables); |
Переменные, которые мы передаем при запуске экземпляра процесса, являются обычными переменными. Они сохраняются, и история аудита будет сохранена, так как нет причины, почему это не должно иметь место.
Первый выполняемый шаг — это шаг «выполнить HTTP-вызов», который представляет собой задачу службы с делегатом Java:
|
1
|
<serviceTask name="Execute HTTP call" activiti:class="org.activiti.ExecuteHttpCallDelegate"></serviceTask> |
Java-код:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class ExecuteHttpCallDelegate implements JavaDelegate { public void execute(DelegateExecution execution) { String keyword = (String) execution.getVariable("keyWord"); String language = (String) execution.getVariable("language"); url = String.format(url, keyword, language); HttpGet httpget = new HttpGet(url); CloseableHttpClient httpclient = HttpClients.createDefault(); try { CloseableHttpResponse response = httpclient.execute(httpget); execution.setTransientVariable("response", IOUtils.toString(response.getEntity().getContent(), "UTF-8")); execution.setTransientVariable("responseStatus", response.getStatusLine().getStatusCode()); response.close(); } catch (Exception e) { e.printStackTrace(); } }} |
Здесь мы выполняем простую HTTP-процедуру с использованием API GitHub, используя переменные «ключевое слово» и «язык», которые мы передали при запуске экземпляра процесса. Особое здесь в строке 16 и 17, что мы храним ответ и статус ответа в переходных переменных (это вызов setTransientVariable () ). Причины выбора переходных переменных здесь
- Ответ json от Github API очень велик. Конечно, его можно хранить постоянным способом, но это не скажется на производительности.
- С точки зрения аудита весь ответ имеет очень мало значения. Мы извлечем важные биты позже из этого ответа, и они будут сохранены в исторических данных.
После получения ответа и сохранения его во временной переменной мы передаем исключительный шлюз. Последовательность потока выглядит следующим образом:
|
1
2
3
4
5
6
|
<sequenceFlow ... > <extensionElements> <activiti:executionListener event="take" class="org.activiti.ProcessResponseExecutionListener"></activiti:executionListener> </extensionElements> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${responseStatus == 200}]]></conditionExpression></sequenceFlow> |
Обратите внимание, что для условия потока последовательности нет никакой разницы, когда речь идет об использовании переходной или непереходной переменной. Обычный getVariable также возвращает временную переменную с именем, если оно установлено (это теневая часть в документах, упомянутых выше). GetTransientVariable также существует, когда следует обращаться только к временному набору переменных. Во всяком случае: для условия: нет разницы вообще.
Вы также можете видеть, что поток последовательности имеет (скрытый на диаграмме) прослушиватель выполнения. Слушатель выполнения проанализирует ответ json, выберет соответствующие биты и сохранит их в списке переходных массивов. Это важно, так как вы прочитаете код ниже.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class ProcessResponseExecutionListener implements ExecutionListener { private ObjectMapper objectMapper = new ObjectMapper(); public void notify(DelegateExecution execution) { List<String> searchResults = new ArrayList<String>(); String response = (String) execution.getVariable("response"); try { JsonNode jsonNode = objectMapper.readTree(response); JsonNode itemsNode = jsonNode.get("items"); if (itemsNode != null && itemsNode.isArray()) { for (JsonNode itemNode : (ArrayNode) itemsNode) { String url = itemNode.get("html_url").asText(); searchResults.add(url); } } } catch (IOException e) { e.printStackTrace(); } execution.setTransientVariable("searchResults", searchResults); }} |
Причина сохранения списка в качестве временной переменной является важной. Как вы можете видеть на диаграмме, следует подпроцесс с несколькими экземплярами. Подпроцесс часто принимает переменную коллекции для создания экземпляров. До сих пор это была постоянная переменная в виде сериализованного с помощью Java ArrayList. Мне никогда не нравилось это (я всегда использовал DelegateExpressions с бобом, если мне приходилось делать это раньше). Это всегда немного беспокоило меня. Теперь массив данных является временным и не будет храниться в хранилище данных:
|
1
2
3
|
<subProcess name="subProcess"> <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="searchResults" activiti:elementVariable="searchResult" /> |
Обратите внимание, что указанная выше переменная searchResult будет постоянной.
Обратите внимание, что временные переменные будут присутствовать до тех пор, пока пользовательская задача не будет достигнута, а состояние не будет сохранено в хранилище данных. Также возможно передавать временные переменные при запуске экземпляра процесса (что могло бы быть и здесь, но я думаю, что сохранение пользовательского ввода — это то, что вам нужно в данных аудита).
Если вы запустите экземпляр процесса, например, так:
|
1
2
3
4
5
6
7
8
9
|
Map<String, Object> variables = new HashMap<String, Object>();variables.put("keyWord", "workflow");variables.put("language", "java");ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("githubsearch", variables);List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();for (Task task : tasks) { System.out.println("Current task : " + task.getName());} |
Что дает в качестве примера вывод (ограничен 5 результатами):
Текущая задача: просмотреть результат https://github.com/Activiti/Activiti
Текущая задача: просмотреть результат https://github.com/twitter/ambrose
Текущая задача: просмотреть результат https://github.com/azkaban/azkaban
Текущая задача: просмотреть результат https://github.com/romannurik/AndroidDesignPreview
Текущая задача: просмотреть результат https://github.com/spring-projects/spring-xd
…
Теперь пользователь может посмотреть детали каждого из результатов и продолжить процесс.
Последние слова
Как вы можете себе представить, существует довольно много вариантов использования для временных переменных. Я знаю, что для многих это была важная особенность, поэтому я рад, что она сейчас там. Отзывы и комментарии конечно, как обычно, всегда приветствуются!
| Ссылка: | Как использовать временные переменные в Activiti от нашего партнера по JCG Джорама Барреса в блоге « Маленькие шаги с большими ногами» . |
