diff --git a/bom/datapool-dependencies/pom.xml b/bom/datapool-dependencies/pom.xml index 575eea33c..c1ba60a5a 100644 --- a/bom/datapool-dependencies/pom.xml +++ b/bom/datapool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../parent/pom.xml diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index fa47fa2ec..57bcfb24c 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-root - 3.10.0 + 3.11.0 ../../pom.xml @@ -18,7 +18,7 @@ https://github.com/holunda-io/camunda-bpm-taskpool/ - 2.7.10 + 2.7.11 7.18.0 4.6.6 @@ -306,7 +306,7 @@ org.jacoco jacoco-maven-plugin - 0.8.9 + 0.8.10 pre-unit-test diff --git a/bom/taskpool-dependencies/pom.xml b/bom/taskpool-dependencies/pom.xml index 510e10b40..d3fcda32b 100644 --- a/bom/taskpool-dependencies/pom.xml +++ b/bom/taskpool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../parent/pom.xml diff --git a/core/bus-jackson/pom.xml b/core/bus-jackson/pom.xml index 14804e34a..89b778c9c 100755 --- a/core/bus-jackson/pom.xml +++ b/core/bus-jackson/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../../bom/parent/pom.xml diff --git a/core/datapool/datapool-api/pom.xml b/core/datapool/datapool-api/pom.xml index fe8de1bf8..6249d1d95 100755 --- a/core/datapool/datapool-api/pom.xml +++ b/core/datapool/datapool-api/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.10.0 + 3.11.0 polyflow-datapool-api diff --git a/core/datapool/datapool-core/pom.xml b/core/datapool/datapool-core/pom.xml index 6fa36ea98..fb4d96fbe 100644 --- a/core/datapool/datapool-core/pom.xml +++ b/core/datapool/datapool-core/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.10.0 + 3.11.0 polyflow-datapool-core diff --git a/core/datapool/datapool-event/pom.xml b/core/datapool/datapool-event/pom.xml index b515de624..28694e943 100755 --- a/core/datapool/datapool-event/pom.xml +++ b/core/datapool/datapool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.10.0 + 3.11.0 polyflow-datapool-event diff --git a/core/datapool/pom.xml b/core/datapool/pom.xml index 6da49f0e0..4c2952aa1 100755 --- a/core/datapool/pom.xml +++ b/core/datapool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../../bom/parent/pom.xml diff --git a/core/taskpool/pom.xml b/core/taskpool/pom.xml index 03cd53162..6e98e9ab9 100755 --- a/core/taskpool/pom.xml +++ b/core/taskpool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../../bom/parent/pom.xml diff --git a/core/taskpool/taskpool-api/pom.xml b/core/taskpool/taskpool-api/pom.xml index c53613086..606a1b419 100755 --- a/core/taskpool/taskpool-api/pom.xml +++ b/core/taskpool/taskpool-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.10.0 + 3.11.0 polyflow-taskpool-api diff --git a/core/taskpool/taskpool-core/pom.xml b/core/taskpool/taskpool-core/pom.xml index 301875e58..e4967e766 100755 --- a/core/taskpool/taskpool-core/pom.xml +++ b/core/taskpool/taskpool-core/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.10.0 + 3.11.0 polyflow-taskpool-core diff --git a/core/taskpool/taskpool-event/pom.xml b/core/taskpool/taskpool-event/pom.xml index ec871c1e2..2cb5b179f 100644 --- a/core/taskpool/taskpool-event/pom.xml +++ b/core/taskpool/taskpool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.10.0 + 3.11.0 polyflow-taskpool-event diff --git a/docs/reference-guide/components/camunda-taskpool-collector.md b/docs/reference-guide/components/camunda-taskpool-collector.md index dc1224a9e..1923afb12 100644 --- a/docs/reference-guide/components/camunda-taskpool-collector.md +++ b/docs/reference-guide/components/camunda-taskpool-collector.md @@ -15,7 +15,7 @@ received from Camunda BPM Engine (from delegate event listener or from history e which is passed over to the Taskpool Collector using internal **Spring eventing** mechanism. The Taskpool Collector converts the series of such events into a Taskpool Command - an entity carrying an intent of change inside the Taskpool core. Please note that _event_ has another meaning in CQRS/ES systems -and other components of the Taskpool, but in the context of Taskpool collector an event always originates from +and other components of the Taskpool, but in the context of Taskpool Collector an event always originates from Spring eventing. ### Features @@ -25,6 +25,7 @@ Spring eventing. - Collection of process variable change events - Collection of task events and history events - Creation of task engine commands +- Collection of tasks assignment information - Enrichment of task engine commands with process variables - Attachment of correlation information to task engine commands - Transmission of commands to Axon command bus @@ -55,7 +56,7 @@ In order to enable collector component, include the Maven dependency to your pro ``` -Then activate the Taskpool collector by providing the annotation on any Spring Configuration: +Then activate the Taskpool Collector by providing the annotation on any Spring Configuration: ```java @Configuration @@ -68,7 +69,7 @@ class MyProcessApplicationConfiguration { ### Event collection -Taskpool Collector registers Spring Event Listener to the following events, fired by Camunda Eventing Engine Plugin: +By default, Taskpool Collector registers Spring Event Listener to the following events, fired by Camunda Eventing Engine Plugin: * `DelegateTask` events: ** create @@ -86,6 +87,22 @@ The events are transformed into corresponding commands and passed over to the pr is fired using custom listeners only and polyflow components don't rely on that but rather on own implementation of built-in (unskippable) listeners. For this purpose, it is important to disable Camunda Platform custom listeners by setting `camunda.bpm.eventing.task` property to `false`. +During collection of task information, you can control which listeners are registered. By default, all listeners are considered but you can change +this behaviour by setting values of two properties: + +```yaml + +polyflow: + integration: + collector: + camunda: + task: + excludedTaskEventNames: assignment, delete + excludedHistoryEventNames: add-identity-link, delete-identity-link +``` +This particular setting is helpful, if you want to disable assignment in the engine entirely and want to provide you own custom task assignment algorithm or +use assignment based on process variables (see below for more details). + ### Task commands enrichment Alongside with attributes received from the Camunda BPM engine, the engine task commands @@ -126,7 +143,7 @@ A `VariableFilter` can be of the following type: Here is an example, how the process variable filter can configure the enrichment: ```java - @Configuration +@Configuration public class MyTaskCollectorConfiguration { @Bean @@ -168,6 +185,12 @@ public class MyTaskCollectorConfiguration { If you want to implement a custom enrichment, please provide your own implementation of the interface `VariablesEnricher` (register a Spring Component of the type) and set the property `polyflow.integration.collector.camunda.task.enricher.type` to `custom`. +!!! warning + Avoid using a classic Camunda `TaskListener` which modifies process variables on task creation, since changes of those + listeners can't be used during task enrichment. A proper way to modify instance or task variables is to implement an ordered Spring + `EventListener` listening on `DelegateTask`, put it before the enricher by providing `@Order(TaskEventCollectorService.ORDER - 80)` and scope the event listener to + the task of your interest using condition: `@EventListener(condition = "#delegateTask.taskDefinitionKey.equals('my-task-key') && #delegateTask.eventName.equals('create')")` + ### Data Correlation Apart from task payload attached by the enricher, the so-called _Correlation_ with data entries can @@ -251,11 +274,11 @@ and the process reaches the task `task_approve_request`, the task will get the f ### Task Assignment -User task assignment is a core functionality for every process application fostering task oriented work. By default, Taskpool Collector uses +User task assignment is a core functionality for every process application fostering task-oriented work. By default, Taskpool Collector uses information from Camunda User Task and maps that one-to-one to properties of the user task commands. The task attribute `assignee`, `candidateUsers` and `candidateGroups` are mapped to the corresponding attributes automatically. -To control the task assignment mode you can configure taskpool collector using application properties. The property +To control the task assignment mode you can configure Taskpool Collector using application properties. The property `polyflow.integration.collector.camunda.task.assigner.type` has the following values: * `no`: No additional assignment takes place, the Camunda task attributes are used (default) diff --git a/integration/camunda-bpm/engine-client/pom.xml b/integration/camunda-bpm/engine-client/pom.xml index 434fde71d..0c25a9f1b 100644 --- a/integration/camunda-bpm/engine-client/pom.xml +++ b/integration/camunda-bpm/engine-client/pom.xml @@ -4,7 +4,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.10.0 + 3.11.0 polyflow-camunda-bpm-engine-client diff --git a/integration/camunda-bpm/pom.xml b/integration/camunda-bpm/pom.xml index 5fd83dcc6..36609fc99 100644 --- a/integration/camunda-bpm/pom.xml +++ b/integration/camunda-bpm/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../../bom/parent/pom.xml diff --git a/integration/camunda-bpm/springboot-autoconfigure/pom.xml b/integration/camunda-bpm/springboot-autoconfigure/pom.xml index 540959ed4..63d44081e 100755 --- a/integration/camunda-bpm/springboot-autoconfigure/pom.xml +++ b/integration/camunda-bpm/springboot-autoconfigure/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.10.0 + 3.11.0 polyflow-camunda-bpm-springboot-autoconfigure diff --git a/integration/camunda-bpm/springboot-starter/pom.xml b/integration/camunda-bpm/springboot-starter/pom.xml index e9af668f2..f6083f6e4 100755 --- a/integration/camunda-bpm/springboot-starter/pom.xml +++ b/integration/camunda-bpm/springboot-starter/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.10.0 + 3.11.0 polyflow-camunda-bpm-springboot-starter diff --git a/integration/camunda-bpm/taskpool-collector/pom.xml b/integration/camunda-bpm/taskpool-collector/pom.xml index d75cfa45b..80dfc615b 100755 --- a/integration/camunda-bpm/taskpool-collector/pom.xml +++ b/integration/camunda-bpm/taskpool-collector/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.10.0 + 3.11.0 polyflow-camunda-bpm-taskpool-collector diff --git a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/CamundaTaskpoolCollectorProperties.kt b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/CamundaTaskpoolCollectorProperties.kt index 8a930f719..c0a696ce7 100755 --- a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/CamundaTaskpoolCollectorProperties.kt +++ b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/CamundaTaskpoolCollectorProperties.kt @@ -1,6 +1,7 @@ package io.holunda.polyflow.taskpool.collector import io.holunda.polyflow.taskpool.collector.task.assigner.ProcessVariableTaskAssignerMapping +import org.camunda.bpm.engine.delegate.TaskListener import org.springframework.beans.factory.annotation.Value import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.ConstructorBinding @@ -69,9 +70,33 @@ data class CamundaTaskCollectorProperties( * Properties of task importer. */ @NestedConfigurationProperty - val importer: TaskImporterProperties = TaskImporterProperties() + val importer: TaskImporterProperties = TaskImporterProperties(), -) + /** + * List of task events to be excluded from collector. Defaults to empty list, so all events are collected. + * Possible values are constants defined in [TaskListener]. + */ + val excludedTaskEventNames: List = listOf(), + + /** + * List of history events to restrict (HistoricTaskInstanceEventEntity, HistoricIdentityLinkLogEventEntity). Defaults to empty list, so all events are collected. + * Possible values are constants defined in [HistoryEventTypes] + "update". + */ + val excludedHistoryEventNames: List = listOf() +) { + /** + * Determines if the provided event name should be collected. + * @param eventName event name to check. + * @return true if not excluded. + */ + fun collectTaskEvent(eventName: String): Boolean = !excludedTaskEventNames.contains(eventName) + /** + * Determines if the provided event name should be collected. + * @param eventName event name to check. + * @return true if not excluded. + */ + fun collectHistoryEvent(eventName: String): Boolean = !excludedHistoryEventNames.contains(eventName) +} /** * Process variable properties. diff --git a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskCollectorConfiguration.kt b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskCollectorConfiguration.kt index 5b4fc1342..9434e7272 100644 --- a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskCollectorConfiguration.kt +++ b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskCollectorConfiguration.kt @@ -114,7 +114,7 @@ class TaskCollectorConfiguration( /** * Constructs the task collector service responsible for collecting Camunda Spring events and building commands out of them. */ - @Bean + @Bean(TaskEventCollectorService.NAME) fun taskEventCollectorService(repositoryService: RepositoryService) = TaskEventCollectorService( camundaTaskpoolCollectorProperties = camundaTaskpoolCollectorProperties, repositoryService = repositoryService diff --git a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskEventCollectorService.kt b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskEventCollectorService.kt index 5d08ccdc0..5a9b208f0 100755 --- a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskEventCollectorService.kt +++ b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/polyflow/taskpool/collector/task/TaskEventCollectorService.kt @@ -17,12 +17,13 @@ import org.springframework.core.annotation.Order * Collects Camunda events and Camunda historic events (event listener order is {@link TaskEventCollectorService#ORDER}) and emits Commands */ class TaskEventCollectorService( - private val camundaTaskpoolCollectorProperties: CamundaTaskpoolCollectorProperties, + val camundaTaskpoolCollectorProperties: CamundaTaskpoolCollectorProperties, // must not be private to access in conditions of event handlers private val repositoryService: RepositoryService ) { companion object : KLogging() { + const val NAME = "taskEventCollectorService" // high order to be later than all other listeners and work on changed entity const val ORDER = Integer.MAX_VALUE - 100 } @@ -46,7 +47,7 @@ class TaskEventCollectorService( * Fires create command. */ @Order(ORDER) - @EventListener(condition = "#task.eventName.equals('create')") + @EventListener(condition = "#task.eventName.equals('create') && @taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectTaskEvent('create')") fun create(task: DelegateTask): CreateTaskCommand = CreateTaskCommand( id = task.id, @@ -70,7 +71,7 @@ class TaskEventCollectorService( * Fires complete. */ @Order(ORDER) - @EventListener(condition = "#task.eventName.equals('complete')") + @EventListener(condition = "#task.eventName.equals('complete') && @taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectTaskEvent('complete')") fun complete(task: DelegateTask): CompleteTaskCommand = CompleteTaskCommand( id = task.id, @@ -81,17 +82,18 @@ class TaskEventCollectorService( * Fires assign command. */ @Order(ORDER) - @EventListener(condition = "#task.eventName.equals('assignment')") + @EventListener(condition = "#task.eventName.equals('assignment') && @taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectTaskEvent('assignment')") fun assign(task: DelegateTask) { // this method is intentionally empty to demonstrate that the assign event is captured. // we hence rely on historic identity link events to capture assignment via API and via listeners more accurately. + // see implementation below } /** * Fires delete command. */ @Order(ORDER) - @EventListener(condition = "#task.eventName.equals('delete')") + @EventListener(condition = "#task.eventName.equals('delete') && @taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectTaskEvent('delete')") fun delete(task: DelegateTask): DeleteTaskCommand = DeleteTaskCommand( id = task.id, @@ -103,7 +105,7 @@ class TaskEventCollectorService( * Fires update command. */ @Order(ORDER) - @EventListener(condition = "#task.eventName.equals('update')") + @EventListener(condition = "#task.eventName.equals('update') && @taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectTaskEvent('update')") fun update(task: DelegateTask): UpdateAttributeTaskCommand? = if (task is TaskEntity) { if (task.isAssigneeChange()) { @@ -122,10 +124,9 @@ class TaskEventCollectorService( * into the original intent. */ @Order(ORDER) - @EventListener - fun update(changeEvent: HistoricTaskInstanceEventEntity): UpdateAttributesHistoricTaskCommand? = - when (changeEvent.eventType) { - "update" -> UpdateAttributesHistoricTaskCommand( + @EventListener(condition = "#changeEvent.eventType.equals('update') && @taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectHistoryEvent('update')") + fun update(changeEvent: HistoricTaskInstanceEventEntity): UpdateAttributesHistoricTaskCommand = + UpdateAttributesHistoricTaskCommand( id = changeEvent.taskId, description = changeEvent.description, dueDate = changeEvent.dueDate, @@ -137,15 +138,12 @@ class TaskEventCollectorService( sourceReference = changeEvent.sourceReference(repositoryService, camundaTaskpoolCollectorProperties.applicationName) ) - else -> null - } - /** * Fires update assignment historic command. * This is the only way to detect changes of identity links (candidate user/group change and remove). */ @Order(ORDER) - @EventListener + @EventListener(condition = "@taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectHistoryEvent('add-identity-link') || @taskEventCollectorService.camundaTaskpoolCollectorProperties.task.collectHistoryEvent('delete-identity-link')") fun update(changeEvent: HistoricIdentityLinkLogEventEntity): Any? = when { // user assignment. Is needed because the assignment out of a listener is undetected otherwise. diff --git a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/collector/properties/CamundaTaskpoolCollectorPropertiesExtendedTest.kt b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/collector/properties/CamundaTaskpoolCollectorPropertiesExtendedTest.kt index 458d2bd46..d0f1ba439 100644 --- a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/collector/properties/CamundaTaskpoolCollectorPropertiesExtendedTest.kt +++ b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/collector/properties/CamundaTaskpoolCollectorPropertiesExtendedTest.kt @@ -83,6 +83,31 @@ internal class CamundaTaskpoolCollectorPropertiesExtendedTest { } } + @Test + fun `loads properties configuration to ignore listeners`() { + contextRunner + .withUserConfiguration(TestMockConfiguration::class.java) + .withUserConfiguration(AdditionalMockConfiguration::class.java) + .withPropertyValues( + "spring.application.name=my-test-application", + "camunda.bpm.eventing.task=false", + "polyflow.integration.collector.camunda.task.enabled=true", + "polyflow.integration.collector.camunda.task.enricher.type=custom", + "polyflow.integration.collector.camunda.task.excluded-task-event-names=assignment", + "polyflow.integration.collector.camunda.task.excluded-history-event-names=add-identity-link,delete-identity-link", + ).run { + + assertThat(it.getBean(CamundaTaskpoolCollectorProperties::class.java)).isNotNull + val props: CamundaTaskpoolCollectorProperties = it.getBean(CamundaTaskpoolCollectorProperties::class.java) + + assertThat(props.task.enabled).isTrue + assertThat(props.task.collectTaskEvent("assignment")).isFalse() + assertThat(props.task.collectHistoryEvent("add-identity-link")).isFalse() + assertThat(props.task.collectHistoryEvent("delete-identity-link")).isFalse() + } + } + + /** * Config class without configuration annotation not to confuse others. */ diff --git a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/TestDriver.kt b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/TestDriver.kt index e6f6e4870..7b35e6ff1 100644 --- a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/TestDriver.kt +++ b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/TestDriver.kt @@ -145,8 +145,8 @@ class TestDriver( } - fun assertProcessInstanceWaitsInUserTask(instance: ProcessInstance) { - BpmnAwareTests.assertThat(instance).isWaitingAt(TASK_DEFINITION_KEY) + fun assertProcessInstanceWaitsInUserTask(instance: ProcessInstance, taskDefinitionKey: String = TASK_DEFINITION_KEY) { + BpmnAwareTests.assertThat(instance).isWaitingAt(taskDefinitionKey) } /* diff --git a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/tx/TaskCollectorITest.kt b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/tx/TaskCollectorITest.kt index 92dbec1d0..963bf20f6 100644 --- a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/tx/TaskCollectorITest.kt +++ b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/tx/TaskCollectorITest.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.module.kotlin.convertValue import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.holunda.camunda.taskpool.api.task.* import io.holunda.polyflow.taskpool.EnableCamundaTaskpoolCollector +import io.holunda.polyflow.taskpool.collector.task.TaskEventCollectorService import io.holunda.polyflow.taskpool.itest.TestDriver import io.holunda.polyflow.taskpool.itest.TestDriver.Companion.DEFAULT_VARIABLES import io.holunda.polyflow.taskpool.itest.TestDriver.Companion.createTaskCommand @@ -17,6 +18,7 @@ import org.axonframework.commandhandling.gateway.CommandGateway import org.camunda.bpm.engine.RepositoryService import org.camunda.bpm.engine.RuntimeService import org.camunda.bpm.engine.TaskService +import org.camunda.bpm.engine.delegate.DelegateTask import org.camunda.bpm.engine.delegate.TaskListener import org.camunda.bpm.engine.impl.interceptor.Command import org.camunda.bpm.engine.impl.interceptor.CommandExecutor @@ -32,6 +34,8 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Primary +import org.springframework.context.event.EventListener +import org.springframework.core.annotation.Order import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles import java.time.Instant.now @@ -647,6 +651,43 @@ internal class TaskCollectorITest { } + /** + * The process is started and waits in a user task. The user task has a task listener that changes some local process variables on create. + * The create command should contain the local variables. + */ + @Test + fun `updates variables with create listener implemented as spring event handler`() { + + // deploy + driver.deployProcess( + createUserTaskProcess( + taskDefinitionKey = "eventing", + taskListeners = listOf( + "create" to "#{setTaskLocalVariables}" // this one will be ignored, since it is invoked after enrichment + ) + ) + ) + + + // start + val instance = driver.startProcessInstance(variables = Variables.createVariables().putValue("overriddenVariable", "global-value")) + driver.assertProcessInstanceWaitsInUserTask(instance, "eventing") + + val createCommand = createTaskCommand( + variables = Variables.createVariables() + .putValue("taskLocalOnlyVariable", "only-value-by-event-handler") + .putValue("overriddenVariable", "local-value-by-event-handler") + ) + + + waitAtMost(3, TimeUnit.SECONDS).untilAsserted { + verify(commandListGateway).sendToGateway( + listOf(createCommand) + ) + } + + verifyNoMoreInteractions(commandListGateway) + } /** * Internal test application. @@ -694,6 +735,26 @@ internal class TaskCollectorITest { delegateTask.dueDate = TestDriver.NOW delegateTask.followUpDate = TestDriver.NOW } + + /** + * A task listener that sets some local variables. + */ + @Order(TaskEventCollectorService.ORDER - 80) + @EventListener(condition = "#delegateTask.taskDefinitionKey.equals('eventing') && #delegateTask.eventName.equals('create')") + fun setTaskLocalVariables(delegateTask: DelegateTask) { + delegateTask.setVariableLocal("taskLocalOnlyVariable", "only-value-by-event-handler") + delegateTask.setVariableLocal("overriddenVariable", "local-value-by-event-handler") + } + + + /** + * A task listener that sets some local variables. + */ + @Bean + fun setTaskLocalVariables() = TaskListener { delegateTask -> + delegateTask.setVariableLocal("taskLocalOnlyVariable", "only-value") + delegateTask.setVariableLocal("overriddenVariable", "local-value") + } } } diff --git a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/txjob/TaskTxJobSenderITest.kt b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/txjob/TaskTxJobSenderITest.kt index bd5b4d011..eeddff42d 100644 --- a/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/txjob/TaskTxJobSenderITest.kt +++ b/integration/camunda-bpm/taskpool-collector/src/test/kotlin/io/holunda/polyflow/taskpool/itest/txjob/TaskTxJobSenderITest.kt @@ -7,17 +7,16 @@ import io.holunda.polyflow.taskpool.itest.TestDriver import io.holunda.polyflow.taskpool.itest.TestDriver.Companion.createUserTaskProcess import io.holunda.polyflow.taskpool.sender.gateway.CommandListGateway import org.assertj.core.api.Assertions +import org.awaitility.Awaitility import org.axonframework.commandhandling.gateway.CommandGateway import org.camunda.bpm.engine.ManagementService import org.camunda.bpm.engine.RepositoryService import org.camunda.bpm.engine.RuntimeService -import org.camunda.bpm.engine.TaskService -import org.camunda.bpm.engine.impl.interceptor.Command -import org.camunda.bpm.engine.impl.interceptor.CommandExecutor +import org.camunda.bpm.engine.delegate.TaskListener +import org.camunda.bpm.engine.impl.persistence.entity.MessageEntity import org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.* +import org.camunda.bpm.engine.variable.Variables import org.camunda.bpm.spring.boot.starter.annotation.EnableProcessApplication -import org.junit.After -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -30,20 +29,20 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Primary -import org.springframework.test.annotation.Commit import org.springframework.test.annotation.DirtiesContext -import org.springframework.test.annotation.Rollback import org.springframework.test.context.ActiveProfiles -import org.springframework.test.context.transaction.AfterTransaction +import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional -import org.springframework.transaction.support.TransactionSynchronization +import org.springframework.transaction.support.TransactionTemplate import java.util.* +import java.util.concurrent.TimeUnit -@Disabled("Understand how to test this") @SpringBootTest(classes = [TaskTxJobSenderITest.TaskTxJobSenderTestApplication::class], webEnvironment = SpringBootTest.WebEnvironment.MOCK) @ActiveProfiles("txjob-sender-itest") @DirtiesContext +@Transactional +@Disabled("This test sometimes fails because it can't create a new TX (Entity Manager is closed).") internal class TaskTxJobSenderITest { @MockBean @@ -59,63 +58,111 @@ internal class TaskTxJobSenderITest { lateinit var managementService: ManagementService @Autowired - lateinit var taskServiceService: TaskService - - - @Autowired - lateinit var commandExecutor: CommandExecutor + lateinit var txTemplate: TransactionTemplate + lateinit var createCommand: CreateTaskCommand private val driver: TestDriver by lazy { TestDriver(repositoryService, runtimeService) } - @BeforeEach - fun `start process and create user task`() { - + fun `setup tx template`() { + txTemplate.propagationBehavior = Propagation.REQUIRES_NEW.value() } @Test - @Transactional - @Commit fun `creates task in process`() { - // deploy - driver.deployProcess( - createUserTaskProcess() - ) -// commandExecutor.execute { -// Command { - // start - val instance = driver.startProcessInstance() - // instance is started - assertThat(instance).isStarted - // user task - driver.assertProcessInstanceWaitsInUserTask(instance) -// } -// } + doInTransaction { + // deploy + driver.deployProcess( + createUserTaskProcess() + ) + // start + val instance = driver.startProcessInstance() + // instance is started + assertThat(instance).isStarted + // user task + driver.assertProcessInstanceWaitsInUserTask(instance) - /* + verifyNoMoreInteractions(commandListGateway) + + createCommand = TestDriver.createTaskCommand() + } + + assertAndExecuteCommandSendingJob() + + verify(commandListGateway).sendToGateway( + listOf(createCommand) + ) + } - val createCommand = createTaskCommand( - candidateUsers = setOf("piggy"), - candidateGroups = setOf("muppetshow"), + /** + * The process is started and waits in a user task. The user task has a task listener that changes some local process variables on create. + * The create command should contain the local variables. + */ + @Test + @Disabled("Find out why the local listener update always gt into the next TX and how to deal with it") + fun `updates variables with create listener`() { + + doInTransaction { + // deploy + driver.deployProcess( + createUserTaskProcess( + taskListeners = listOf( + "create" to "#{setTaskLocalVariables}" + ) ) - */ + ) + + // start + val instance = driver.startProcessInstance(variables = Variables.createVariables().putValue("overriddenVariable", "global-value")) + driver.assertProcessInstanceWaitsInUserTask(instance) + + verifyNoMoreInteractions(commandListGateway) + createCommand = TestDriver.createTaskCommand( + variables = Variables.createVariables() + .putValue("taskLocalOnlyVariable", "only-value") + .putValue("overriddenVariable", "local-value") + ) + } - verifyNoMoreInteractions(commandListGateway) + assertAndExecuteCommandSendingJob() + + verify(commandListGateway).sendToGateway( + listOf(createCommand) + ) } - @AfterTransaction - fun `after all`() { - val jobs = managementService.createJobQuery().list() - Assertions.assertThat(jobs).hasSize(1) + private fun assertAndExecuteCommandSendingJob() { + doInTransaction { + val jobs = managementService.createJobQuery().list() + Assertions.assertThat(jobs).hasSize(1) + Assertions.assertThat(jobs[0]).isInstanceOf(MessageEntity::class.java) + Assertions.assertThat((jobs[0] as MessageEntity).jobHandlerType).isEqualTo("polyflow-engine-task-command-sending") + + Awaitility.waitAtMost(3, TimeUnit.SECONDS).untilAsserted { + try { + execute(jobs[0]) + } catch (e: Exception) { + // brute force preventing Optimistic locking exception, IllegalStateException (job doesn't exist) + } + Assertions.assertThat(managementService.createJobQuery().count()).isEqualTo(0) + } + } } + private fun doInTransaction(operation: Runnable) { + txTemplate.execute { + operation.run() + null + } + + } @SpringBootApplication @EnableProcessApplication @@ -126,5 +173,15 @@ internal class TaskTxJobSenderITest { @Bean @Primary fun testTxJobAxonCommandGateway(): CommandGateway = mock() + + /** + * A task listener that sets some local variables. + */ + @Bean + fun setTaskLocalVariables() = TaskListener { delegateTask -> + delegateTask.setVariableLocal("taskLocalOnlyVariable", "only-value") + delegateTask.setVariableLocal("overriddenVariable", "local-value") + } + } } diff --git a/integration/camunda-bpm/taskpool-collector/src/test/resources/application-txjob-sender-itest.yml b/integration/camunda-bpm/taskpool-collector/src/test/resources/application-txjob-sender-itest.yml index 6c3a453c9..2b0e97a03 100644 --- a/integration/camunda-bpm/taskpool-collector/src/test/resources/application-txjob-sender-itest.yml +++ b/integration/camunda-bpm/taskpool-collector/src/test/resources/application-txjob-sender-itest.yml @@ -1,5 +1,5 @@ spring: - application.name: txjob-sender-test + application.name: collector-test jpa: open-in-view: true # disable JPA warning show-sql: true @@ -17,7 +17,16 @@ camunda: polyflow: integration: + collector: + camunda: + task: + enabled: true + process-instance: + enabled: false + process-definition: + enabled: false sender: task: type: txjob send-within-transaction: true + diff --git a/integration/camunda-bpm/taskpool-job-sender/pom.xml b/integration/camunda-bpm/taskpool-job-sender/pom.xml index f5ee9f19f..f6f7b48d1 100755 --- a/integration/camunda-bpm/taskpool-job-sender/pom.xml +++ b/integration/camunda-bpm/taskpool-job-sender/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.10.0 + 3.11.0 polyflow-camunda-bpm-taskpool-job-sender diff --git a/integration/common/datapool-sender/pom.xml b/integration/common/datapool-sender/pom.xml index 06f83b9f7..dc304de41 100755 --- a/integration/common/datapool-sender/pom.xml +++ b/integration/common/datapool-sender/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.10.0 + 3.11.0 polyflow-datapool-sender diff --git a/integration/common/pom.xml b/integration/common/pom.xml index acce6c5aa..20bc0ed7d 100755 --- a/integration/common/pom.xml +++ b/integration/common/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../../bom/parent/pom.xml diff --git a/integration/common/tasklist-url-resolver/pom.xml b/integration/common/tasklist-url-resolver/pom.xml index 92158c1a1..c625b9d1a 100644 --- a/integration/common/tasklist-url-resolver/pom.xml +++ b/integration/common/tasklist-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.10.0 + 3.11.0 polyflow-tasklist-url-resolver diff --git a/integration/common/taskpool-sender/pom.xml b/integration/common/taskpool-sender/pom.xml index 1d06c591f..10cadc2ad 100755 --- a/integration/common/taskpool-sender/pom.xml +++ b/integration/common/taskpool-sender/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.10.0 + 3.11.0 polyflow-taskpool-sender diff --git a/integration/common/variable-serializer/pom.xml b/integration/common/variable-serializer/pom.xml index 6a5fc6579..e81865208 100755 --- a/integration/common/variable-serializer/pom.xml +++ b/integration/common/variable-serializer/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.10.0 + 3.11.0 polyflow-variable-serializer diff --git a/pom.xml b/pom.xml index e95b93f2b..27b13a831 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-root - 3.10.0 + 3.11.0 pom POM: ${project.artifactId} @@ -20,7 +20,7 @@ ${java.version} ${java.version} - 1.8.20 + 1.8.21 ${java.version} true 3.0.5 diff --git a/view/form-url-resolver/pom.xml b/view/form-url-resolver/pom.xml index d08aeeb3b..a3db7b34c 100644 --- a/view/form-url-resolver/pom.xml +++ b/view/form-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.10.0 + 3.11.0 polyflow-form-url-resolver diff --git a/view/jpa/pom.xml b/view/jpa/pom.xml index 5527fefe7..e21baaff1 100644 --- a/view/jpa/pom.xml +++ b/view/jpa/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.10.0 + 3.11.0 polyflow-view-jpa diff --git a/view/mongo/pom.xml b/view/mongo/pom.xml index 40d451fb5..ba510a6e4 100755 --- a/view/mongo/pom.xml +++ b/view/mongo/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.10.0 + 3.11.0 polyflow-view-mongo diff --git a/view/pom.xml b/view/pom.xml index a7e7ca730..672ff531c 100644 --- a/view/pom.xml +++ b/view/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.10.0 + 3.11.0 ../bom/parent/pom.xml diff --git a/view/simple/pom.xml b/view/simple/pom.xml index 2a7cdaf54..254c8ee36 100755 --- a/view/simple/pom.xml +++ b/view/simple/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-view-parent - 3.10.0 + 3.11.0 polyflow-view-simple diff --git a/view/view-api-client/pom.xml b/view/view-api-client/pom.xml index 6314f3e87..773d04061 100755 --- a/view/view-api-client/pom.xml +++ b/view/view-api-client/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.10.0 + 3.11.0 polyflow-view-api-client diff --git a/view/view-api/pom.xml b/view/view-api/pom.xml index a71bc672b..eb717e0d5 100755 --- a/view/view-api/pom.xml +++ b/view/view-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.10.0 + 3.11.0 polyflow-view-api