diff --git a/README.adoc b/README.adoc
index 4d12a65d..8694d8f6 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,4 +1,4 @@
-[cols="a,a,a,a,a,a"]
+[cols="a,a,a,a,a,a,a"]
|===
| // ci
image::https://github.com/holunda-io/camunda-bpm-data/workflows/default/badge.svg[caption="Build Status", link=https://github.com/holunda-io/camunda-bpm-data/actions]
@@ -12,22 +12,209 @@ image::https://api.codacy.com/project/badge/Grade/653136bd5cad48c8a9f2621ee304ff
image::https://img.shields.io/badge/License-Apache%202.0-blue.svg[caption="License", link="https://www.holunda.io/camunda-bpm-data/license"]
| // changelog
image::https://img.shields.io/badge/CHANGES----blue.svg[caption="Change log" link="https://www.holunda.io/camunda-bpm-data/changelog"]
+| // gitter
+image::https://badges.gitter.im/holunda-io/camunda-bpm-data.svg[caption="Gitter", link="https://gitter.im/holunda-io/camunda-bpm-data?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge"]
|===
+== Camunda BPM Data
-== Quick Start
+> Beautiful process data handling for Camunda BPM.
-If you just want to start using the library, please consult our link:https://www.holunda.io/camunda-bpm-data/quick-start[Quick Start]
-guide.
+== Why to use this library in every Camunda project?
-== User Guide
+If you are a software engineer and run process automation projects in your company or on behalf of the customer
+based on Camunda Process Engine, you probably are familiar with process variables. Camunda offers an API to access
+them and thereby manipulate the state of the process execution - one of the core features during process automation.
-If you have any questions regarding configuration of Camunda BPM Data please
-have a look to our primary documentation - the link:https://www.holunda.io/camunda-bpm-data/wiki/user-guide[User Guide].
+Unfortunately, as a user of the Camunda API, you have to exactly know the variable type (so the Java class behind it).
+For example, if you store a String in a variable `"orderId"` you must extract it as a String in every piece of code.
+Since there is no code connection between the different code parts, but the BPMN process model orchestrates
+these snippets to a single process execution, it makes refactoring and testing of process automation projects
+error-prone and challenging.
+
+This library helps you to overcome these difficulties and make access, manipulation and testing process variables really
+easy and convenient. We leverage the Camunda API and offer you not only a better API but also some link:https://www.holunda.io/camunda-bpm-data/wiki/user-guide/features[additional features].
+
+If you want to read more about data in Camunda processes, have a look on those articles:
+
+- https://medium.com/holisticon-consultants/data-in-process-part-1-2620bf9abd76[Data in Process (Part 1)]
+- https://medium.com/holisticon-consultants/data-in-process-part-2-7c6a109e6ee2[Data in Process (Part 2)]
+
+
+== Quick Introduction
+
+=== Setup
+If you just want to start using the library, put the following dependency into your project `pom.xml`:
+
+[source,xml]
+----
+
+ io.holunda.data
+ camunda-bpm-data
+ 1.0.0
+
+----
+
+If you are using Gradle Kotlin DSL add to your `build.gradle`:
+[source,kotlin]
+----
+implementation("io.holunda.data:camunda-bpm-data:1.0.0")
+----
+
+For Gradle Groovy DSL add to your `build.gradle`:
+[source,groovy]
+----
+implementation 'io.holunda.data:camunda-bpm-data:1.0.0'
+----
+
+=== Variable declaration
+Now your setup is completed and you can declare your variables like this:
+
+[source,java]
+----
+
+import io.holunda.camunda.bpm.data.factory.VariableFactory;
+import static io.holunda.camunda.bpm.data.CamundaBpmData.*;
+
+public class OrderApproval {
+ public static final VariableFactory ORDER_ID = stringVariable("orderId");
+ public static final VariableFactory ORDER = customVariable("order", Order.class);
+ public static final VariableFactory ORDER_APPROVED = booleanVariable("orderApproved");
+ public static final VariableFactory ORDER_TOTAL = customVariable("orderTotal", BigDecimal.class);
+ public static final VariableFactory ORDER_POSITION = customVariable("orderPosition", OrderPosition.class);
+}
+----
+
+=== Variable access from Java Delegate
+And finally, you want to access them from your Java delegates, Execution or Task Listeners or simple Java components:
+
+[source,java]
+----
+public class MyDelegate implements JavaDelegate {
+ @Override
+ public void execute(DelegateExecution execution) {
+ VariableReader reader = CamundaBpmData.reader(execution);
+ OrderPosition orderPosition = reader.get(ORDER_POSITION);
+ BigDecimal oldTotal = reader.getOptional(ORDER_TOTAL).orElse(BigDecimal.ZERO);
+
+ BigDecimal newTotal = oldTotal.add(calculatePrice(orderPosition));
+ ORDER_TOTAL.on(execution).setLocal(newTotal);
+ }
+
+ private BigDecimal calculatePrice(OrderPosition orderPosition) {
+ return orderPosition.getNetCost().multiply(BigDecimal.valueOf(orderPosition.getAmount()));
+ }
+}
+----
+
+=== Variable access from REST Controller
+
+Now imagine you are implementing a REST controller for a user task form which
+loads data from the process application, displays it, captures some input and
+sends that back to the process application to complete the user task. By doing so,
+you will usually need to access process variables. Here is an example:
+
+[source, java]
+----
+@RestController
+@RequestMapping("/task/approve-order")
+public class ApproveOrderTaskController {
+
+ private final TaskService taskService;
+
+ public ApproveOrderTaskController(TaskService taskService) {
+ this.taskService = taskService;
+ }
+
+ @GetMapping("/{taskId}")
+ public ResponseEntity loadTask(@PathVariable("taskId") String taskId) {
+ Order order = ORDER.from(taskService, taskId).get();
+ return ResponseEntity.ok(new ApproveTaskDto(order));
+ }
+
+ @PostMapping("/{taskId}")
+ public ResponseEntity completeTask(@PathVariable("taskId") String taskId, @RequestBody ApproveTaskCompleteDto userInput) {
+ VariableMap vars = builder()
+ .set(ORDER_APPROVED, userInput.getApproved())
+ .build();
+ taskService.complete(taskId, vars);
+ return ResponseEntity.noContent().build();
+ }
+}
+
+----
+
+=== Testing correct variable access
+
+If you want to write the test for the REST controller, you will need to stub
+the task service and verify that the correct variables has been set. To simplify
+these tests, we created an additional library module `camunda-bpm-data-test`.
+Please put the following dependency into your `pom.xml`:
+[source,xml]
+----
+
+ io.holunda.data
+ camunda-bpm-data-test
+ 1.0.0
+ test
+
+----
+
+Now you can use `TaskServiceVariableMockBuilder` to stub correct behavior of Camunda Task Service
+and `TaskServiceVerifier` to verify the correct access to variables easily. Here is the JUnit
+test of the REST controller above, making use of `camunda-bpm-data-test`.
+
+[source,java]
+----
+public class ApproveOrderTaskControllerTest {
+
+ private static Order order = new Order("ORDER-ID-1", new Date(), new ArrayList<>());
+ private TaskService taskService = mock(TaskService.class);
+ private TaskServiceMockVerifier verifier = taskServiceMockVerifier(taskService);
+ private ApproveOrderTaskController controller = new ApproveOrderTaskController(taskService);
+ private String taskId;
+
+ @Before
+ public void prepareTest() {
+ reset(taskService);
+ taskId = UUID.randomUUID().toString();
+ }
+
+ @Test
+ public void testLoadTask() {
+ // given
+ taskServiceVariableMockBuilder(taskService).initial(ORDER, order).build();
+ // when
+ ResponseEntity responseEntity = controller.loadTask(taskId);
+ // then
+ assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(responseEntity.getBody()).isEqualTo(new ApproveTaskDto(order));
+ verifier.verifyGet(ORDER, taskId);
+ verifier.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testCompleteTask() {
+ // when
+ ApproveTaskCompleteDto response = new ApproveTaskCompleteDto(true);
+ ResponseEntity responseEntity = controller.completeTask(taskId, response);
+ // then
+ assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
+ verifier.verifyComplete(builder().set(ORDER_APPROVED, response.getApproved()).build(), taskId);
+ verifier.verifyNoMoreInteractions();
+ }
+}
+----
+
+=== Further documentation
+
+For further details, please consult our link:https://www.holunda.io/camunda-bpm-data/quick-start[Quick Start]
+guide or have a look to our primary documentation - link:https://www.holunda.io/camunda-bpm-data/wiki/user-guide[the User Guide].
== Working Example
-See our link:https://www.holunda.io/camunda-bpm-data/wiki/user-guide/examples[Examples] for usage and configuration.
+We prepared some typical usage scenarios and implemented two example projects in Java and Kotlin.
+See our link:https://www.holunda.io/camunda-bpm-data/wiki/user-guide/examples[Examples] section for usage and configuration.
== License
diff --git a/docs/pom.xml b/docs/pom.xml
index 94b634a0..8111802d 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -6,7 +6,7 @@
io.holunda.data
camunda-bpm-data-parent
- 0.0.6
+ 1.0.0
camunda-bpm-data-docs
diff --git a/docs/src/orchid/resources/changelog/1.0/1.0.0.ad b/docs/src/orchid/resources/changelog/1.0/1.0.0.ad
new file mode 100644
index 00000000..5b7de7d9
--- /dev/null
+++ b/docs/src/orchid/resources/changelog/1.0/1.0.0.ad
@@ -0,0 +1,24 @@
+---
+version: 1.0.0
+---
+
+== News
+
+* Run through a internal review in Holisticon BPM expert team
+
+== Breaking changes
+
+* The `CamundaBpmData.builder(xxx)` methods and implementing classes where renamed to `XxxWriter` since where didn't follow the Builder pattern. #49
+* The `camunda-bpm-data-kotlin` module is removed and its content is now a part of the `camunda-bpm-data` JAR
+* The `camunda-bpm-data-mockito` module is renamed into `camunda-bpm-data-test` and will contain helpers for testing.
+
+== Features
+* Better documentation and catchy README
+* Implementation of Anti-Corruption-Layer #47
+* VariableReader convenience methods to access multiple variables have been implemented, see #46
+* Provided type collection detector for SPIN/Jackson (currently as PR for SPIN and example code)
+
+== Bugfixes
+
+* Evaluation of complex guards fail if exists condition isn't fulfilled.
+* NPE passing null to set, see #46
diff --git a/docs/src/orchid/resources/homepage.md b/docs/src/orchid/resources/homepage.md
index 3d595bd3..d86ced7b 100644
--- a/docs/src/orchid/resources/homepage.md
+++ b/docs/src/orchid/resources/homepage.md
@@ -4,12 +4,23 @@ layout: frontPage
## Why should I use this?
-This library provides convenient tooling for access to process variables in Camunda BPM engine.
+If you are a software engineer and run process automation projects in your company or on behalf of the customer
+based on Camunda Process Engine, you probably are familiar with process variables. Camunda offers an API to access
+them and thereby manipulate the state of the process execution - one of the core features during process automation.
+
+Unfortunately, as a user of the Camunda API, you have to exactly know the variable type (so the Java class behind it).
+For example, if you store a String in a variable `"orderId"` you must extract it as a String in every piece of code.
+Since there is no code connection between the different code parts, but the BPMN process model orchestrates
+these snippets to a single process execution, it makes refactoring and testing of process automation projects
+error-prone and challenging.
+
+This library helps you to overcome these difficulties and make access, manipulation and testing process variables really
+easy and convenient. We leverage the Camunda API and offer you not only a better API but also some [additional features](https://www.holunda.io/camunda-bpm-data/wiki/user-guide/features).
## How to start?
A good starting point is the [Quick Start](./quick-start). For more detailed documentation, please have a look at
-[User Guide](./wiki/user-guide).
+[User Guide](./wiki/user-guide).
## Get in touch
diff --git a/docs/src/orchid/resources/pages/quick-start.ad b/docs/src/orchid/resources/pages/quick-start.ad
index e0765970..329f2650 100644
--- a/docs/src/orchid/resources/pages/quick-start.ad
+++ b/docs/src/orchid/resources/pages/quick-start.ad
@@ -16,20 +16,20 @@ In Apache Maven add to your `pom.xml`:
io.holunda.data
camunda-bpm-data
- 0.0.5
+ 1.0.0
----
For Gradle Kotlin DSL add to your `build.gradle`:
[source,kotlin]
----
-implementation("io.holunda.data:camunda-bpm-data:0.0.5")
+implementation("io.holunda.data:camunda-bpm-data:1.0.0")
----
For Gradle Groovy DSL add to your `build.gradle`:
[source,groovy]
----
-implementation 'io.holunda.data:camunda-bpm-data:0.0.5'
+implementation 'io.holunda.data:camunda-bpm-data:1.0.0'
----
=== Declare process variable factories
@@ -54,7 +54,7 @@ public class OrderApproval {
}
----
-=== Access process variables
+=== Access process variables from Java Delegate
If you want to access the process variable, call methods on the `ProcessVariableFactory` to configure the usage context,
and then invoke the variable access methods.
@@ -79,3 +79,102 @@ class JavaDelegates {
}
}
----
+
+=== Variable access from REST Controller
+
+Now imagine you are implementing a REST controller for a user task form which
+loads data from the process application, displays it, captures some input and
+sends that back to the process application to complete the user task. By doing so,
+you will usually need to access process variables. Here is an example:
+
+[source, java]
+----
+@RestController
+@RequestMapping("/task/approve-order")
+public class ApproveOrderTaskController {
+
+ private final TaskService taskService;
+
+ public ApproveOrderTaskController(TaskService taskService) {
+ this.taskService = taskService;
+ }
+
+ @GetMapping("/{taskId}")
+ public ResponseEntity loadTask(@PathVariable("taskId") String taskId) {
+ Order order = ORDER.from(taskService, taskId).get();
+ return ResponseEntity.ok(new ApproveTaskDto(order));
+ }
+
+ @PostMapping("/{taskId}")
+ public ResponseEntity completeTask(@PathVariable("taskId") String taskId, @RequestBody ApproveTaskCompleteDto userInput) {
+ VariableMap vars = builder()
+ .set(ORDER_APPROVED, userInput.getApproved())
+ .build();
+ taskService.complete(taskId, vars);
+ return ResponseEntity.noContent().build();
+ }
+}
+
+----
+
+=== Testing correct variable access
+
+If you want to write the test for the REST controller, you will need to stub
+the task service and verify that the correct variables has been set. To simplify
+these tests, we created an additional library module `camunda-bpm-data-test`.
+Please put the following dependency into your `pom.xml`:
+[source,xml]
+----
+
+ io.holunda.data
+ camunda-bpm-data-test
+ 1.0.0
+ test
+
+----
+
+Now you can use `TaskServiceVariableMockBuilder` to stub correct behavior of Camunda Task Service
+and `TaskServiceVerifier` to verify the correct access to variables easily. Here is the JUnit
+test of the REST controller above, making use of `camunda-bpm-data-test`.
+
+[source,java]
+----
+public class ApproveOrderTaskControllerTest {
+
+ private static Order order = new Order("ORDER-ID-1", new Date(), new ArrayList<>());
+ private TaskService taskService = mock(TaskService.class);
+ private TaskServiceMockVerifier verifier = taskServiceMockVerifier(taskService);
+ private ApproveOrderTaskController controller = new ApproveOrderTaskController(taskService);
+ private String taskId;
+
+ @Before
+ public void prepareTest() {
+ reset(taskService);
+ taskId = UUID.randomUUID().toString();
+ }
+
+ @Test
+ public void testLoadTask() {
+ // given
+ taskServiceVariableMockBuilder(taskService).initial(ORDER, order).build();
+ // when
+ ResponseEntity responseEntity = controller.loadTask(taskId);
+ // then
+ assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(responseEntity.getBody()).isEqualTo(new ApproveTaskDto(order));
+ verifier.verifyGet(ORDER, taskId);
+ verifier.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testCompleteTask() {
+ // when
+ ApproveTaskCompleteDto response = new ApproveTaskCompleteDto(true);
+ ResponseEntity responseEntity = controller.completeTask(taskId, response);
+ // then
+ assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
+ verifier.verifyComplete(builder().set(ORDER_APPROVED, response.getApproved()).build(), taskId);
+ verifier.verifyNoMoreInteractions();
+ }
+}
+----
diff --git a/docs/src/orchid/resources/wiki/developer-guide/project-setup.ad b/docs/src/orchid/resources/wiki/developer-guide/project-setup.ad
index 5a329238..56015e02 100644
--- a/docs/src/orchid/resources/wiki/developer-guide/project-setup.ad
+++ b/docs/src/orchid/resources/wiki/developer-guide/project-setup.ad
@@ -58,7 +58,7 @@ If you want to skip the build of examples, please specify the `-DskipExamples` s
We are using https://github.com/orchidhq/Orchid[JavaEden Orchid] for generation of a static site documentation and rely on AsciiDoc as much as possible.
-TIP: If you want to develop your docs in 'live' mode, run `./mvnw -f docs -Pdocs-serve` and access
+TIP: If you want to develop your docs in 'live' mode, run `./mvnw -f docs -Pserve-docs` and access
the http://localhost:8080/ from your browser.
For creation of documentation, please run:
@@ -108,7 +108,7 @@ Currently, the following modules are released to OSS Maven Central:
* camunda-bpm-data-parent
* camunda-bpm-data
-* camunda-bpm-data-kotlin
+* camunda-bpm-data-test
==== Trigger new release
@@ -131,6 +131,25 @@ You can build a release with:
This will update the versions in the `pom.xml` s accordingly and push the release tag to the `master` branch
and update the `develop` branch for the new development version.
+
+==== Create feature for development
+
+You can create a feature branch for development using:
+
+[source,sh]
+----
+./mvnw gitflow:feature-start
+----
+
+WARNING: This operation requires special permissions.
+
+After the feature is complete, create a PR. To merge the PR into develop use the command:
+
+[source,sh]
+----
+./mvnw gitflow:feature-finish
+----
+
==== Trigger a deploy
WARNING: This operation requires special permissions.
diff --git a/docs/src/orchid/resources/wiki/user-guide/examples-java.ad b/docs/src/orchid/resources/wiki/user-guide/examples-java.ad
index 85074bec..4429d226 100644
--- a/docs/src/orchid/resources/wiki/user-guide/examples-java.ad
+++ b/docs/src/orchid/resources/wiki/user-guide/examples-java.ad
@@ -103,7 +103,7 @@ class JavaDelegates {
public ExecutionListener removeProcessVariables() {
return execution ->
{
- CamundaBpmData.builder(execution)
+ CamundaBpmData.writer(execution)
.remove(ORDER_ID)
.remove(ORDER)
.remove(ORDER_APPROVED)
@@ -127,20 +127,20 @@ class SomeService {
private TaskService taskService;
public void setNewValuesForExecution(String executionId, String orderId, Boolean orderApproved) {
- CamundaBpmData.builder(runtimeService, executionId)
+ CamundaBpmData.writer(runtimeService, executionId)
.set(ORDER_ID, orderId)
.set(ORDER_APPROVED, orderApproved)
.update(ORDER_TOTAL, amount -> amount.add(10));
}
public void setNewValuesForTask(String taskId, String orderId, Boolean orderApproved) {
- CamundaBpmData.builder(taskService, taskId)
+ CamundaBpmData.writer(taskService, taskId)
.set(ORDER_ID, orderId)
.set(ORDER_APPROVED, orderApproved);
}
public void start() {
- VariableMap variables = CamundaBpmData.builder()
+ VariableMap variables = CamundaBpmData.writer()
.set(ORDER_ID, "4711")
.set(ORDER_APPROVED, false)
.build();
@@ -149,6 +149,86 @@ class SomeService {
}
----
+=== Fluent API to read several variables
+
+[source,java]
+----
+@Component
+class SomeService {
+
+ @Autowired
+ private RuntimeService runtimeService;
+ @Autowired
+ private TaskService taskService;
+
+ public String readValuesFromExecution(String executionId) {
+ VariableReader reader = CamundaBpmData.reader(runtimeService, executionId);
+ String orderId = reader.get(ORDER_ID);
+ Boolean orderApproved = reader.get(ORDER_APPROVED);
+ if (orderApproved) {
+ // ...
+ }
+ return orderId;
+ }
+
+ public String readValuesFromTask(String taskId) {
+ VariableReader reader = CamundaBpmData.reader(taskService, taskId);
+ String orderId = reader.get(ORDER_ID);
+ Boolean orderApproved = reader.get(ORDER_APPROVED);
+ if (orderApproved) {
+ // ...
+ }
+ return orderId;
+ }
+}
+----
+
+
+=== Anti-Corruption-Layer: Wrap variables to correlate
+
+[source,java]
+----
+@Component
+class SomeService {
+
+ private static final AntiCorruptionLayer MESSAGE_ACL = CamundaBpmDataMapper.identityReplace(
+ "__transient",
+ true
+ );
+
+ public void correlate() {
+ VariableMap variables = CamundaBpmData.builder()
+ .set(ORDER_ID, "4711")
+ .set(ORDER_APPROVED, false)
+ .build();
+ runtimeService.correlateMessage("message_1", MESSAGE_ACL.wrap(variables));
+ }
+}
+----
+
+=== Anti-Corruption-Layer: Check and wrap variables to correlate
+
+[source,java]
+----
+@Component
+class SomeService {
+
+ private static AntiCorruptionLayer MY_ACL = CamundaBpmDataACL.guardTransformingReplace(
+ "__transient",
+ true,
+ new VariablesGuard(exists(ORDER_ID)),
+ IdentityVariableMapTransformer.INSTANCE
+ );
+
+ public void correlate() {
+ VariableMap variables = CamundaBpmData.builder()
+ .set(ORDER_ID, "4711")
+ .set(ORDER_APPROVED, false)
+ .build();
+ runtimeService.correlateMessage("message_1", MESSAGE_ACL.checkAndWrap(variables));
+ }
+}
+----
=== Example project
diff --git a/docs/src/orchid/resources/wiki/user-guide/examples-kotlin.ad b/docs/src/orchid/resources/wiki/user-guide/examples-kotlin.ad
index af32e496..38de79c7 100644
--- a/docs/src/orchid/resources/wiki/user-guide/examples-kotlin.ad
+++ b/docs/src/orchid/resources/wiki/user-guide/examples-kotlin.ad
@@ -122,14 +122,14 @@ class SomeService(
) {
fun setNewValuesForExecution(executionId: String, rderId: String, orderApproved: Boolean) {
- runtimeService.builder(executionId)
+ runtimeService.writer(executionId)
.set(ORDER_ID, orderId)
.set(ORDER_APPROVED, orderApproved)
.update(ORDER_TOTAL, { amount -> amount.add(10) })
}
fun setNewValuesForTask(taskId: String, orderId: String, orderApproved: Boolean) {
- taskService.builder(taskId)
+ taskService.writer(taskId)
.set(ORDER_ID, orderId)
.set(ORDER_APPROVED, orderApproved)
}
@@ -143,6 +143,81 @@ class SomeService(
}
----
+=== Fluent API to read several variables
+
+[source,kotlin]
+----
+@Component
+class SomeService(
+ private val runtimeService: RuntimeService,
+ private val taskService: TaskService
+) {
+
+ fun readValuesFromExecution(executionId: String): String {
+ val reader = CamundaBpmData.reader(runtimeService, executionId)
+ val orderId = reader.get(ORDER_ID)
+ val orderApproved = reader.get(ORDER_APPROVED)
+ if (orderApproved) {
+ // ...
+ }
+ return orderId
+ }
+
+ fun readValuesFromTask(taskId: String ): String {
+ val reader = CamundaBpmData.reader(taskService, taskId)
+ val orderId = reader.get(ORDER_ID)
+ val orderApproved = reader.get(ORDER_APPROVED)
+ if (orderApproved) {
+ // ...
+ }
+ return orderId
+ }
+}
+----
+
+=== Anti-Corruption-Layer: Wrap variables to correlate
+
+[source,kotlin]
+----
+@Component
+class SomeService {
+
+ val MESSAGE_ACL = CamundaBpmDataMapper.identityReplace("__transient", true);
+
+ fun correlate() {
+ val variables = CamundaBpmData.builder()
+ .set(ORDER_ID, "4711")
+ .set(ORDER_APPROVED, false)
+ .build();
+ runtimeService.correlateMessage("message_1", MESSAGE_ACL.wrap(variables));
+ }
+}
+----
+
+=== Anti-Corruption-Layer: Check and wrap variables to correlate
+
+[source,kotlin]
+----
+@Component
+class SomeService {
+
+ val MY_ACL = CamundaBpmDataACL.guardTransformingReplace(
+ "__transient",
+ true,
+ VariablesGuard(exists(ORDER_ID)),
+ IdentityVariableMapTransformer
+ );
+
+ fun correlate() {
+ val variables = CamundaBpmData.writer()
+ .set(ORDER_ID, "4711")
+ .set(ORDER_APPROVED, false)
+ .build();
+ runtimeService.correlateMessage("message_1", MESSAGE_ACL.checkAndWrap(variables));
+ }
+}
+----
+
=== Example project
diff --git a/docs/src/orchid/resources/wiki/user-guide/features.ad b/docs/src/orchid/resources/wiki/user-guide/features.ad
index fa4f5210..922a3cbb 100644
--- a/docs/src/orchid/resources/wiki/user-guide/features.ad
+++ b/docs/src/orchid/resources/wiki/user-guide/features.ad
@@ -5,15 +5,14 @@ title: Features
== {{ page.title }}
* Process Variables
-- The library provides a way to construct generic adapter for every process variable.
-- The adapter contains variable name.
+- The library provides a way to construct a generic adapter for every process variable.
- The adapter contains variable type.
- The adapter can be applied in any context (`RuntimeService`, `TaskService`, `DelegateExecution`, `DelegateTask`, `VariableMap`).
- The adapter offers methods to read, write, update and remove variable values.
- The adapter works for all types supported by Camunda BPM. This includes primitive types, object and container types ( `List`, `Set`, `Map` ).
- The adapter supports global / local variables.
- The adapter support transient variables.
-- Fluent builders are available in order to set, remove or update multiple variables in the same context.
+- Fluent API helper are available in order to set, remove or update multiple variables in the same context (`VariableMapBuilder`, `VariableReader` and `VariableWriter`).
* Process Variable Guards
- Generic support for `VariableGuard` for evaluation of a list of `VariableCondition`s
- Condition to check if variable exists.
@@ -21,9 +20,17 @@ title: Features
- Condition to check if variable has a predefined value.
- Condition to check if variable has one of predefined values.
- Condition to check if variable matches condition specified by a custom function.
-- `AbstractGuardTaskListener` to construct variable conditions guards easily.
-- `AbstractGuardExecutionListener` to construct variable conditions guards easily.
+- `DefaultGuardTaskListener` to construct variable conditions guards easily.
+- `DefaultGuardExecutionListener` to construct variable conditions guards easily.
+* Anti-Corruption-Layer
+- Generic support for `AntiCorruptionLayer` for protection and influence of variable modification in signalling and message correlation.
+- Helper methods for the client to wrap variables in a transient carrier.
+- Execution listener to handle `VariableGuard`-based conditions and `VariableMapTransformer`-based modifications.
+- Task listener to handle `VariableGuard`-based conditions and `VariableMapTransformer`-based modifications.
+- Factory methods to create `AntiCorruptionLayer` with a `VariableGuard` (see `CamundaBpmDataACL`)
+- Factory methods to create `AntiCorruptionLayer` without a `VariableGuard` (see `CamundaBpmDataMapper`)
* Testing variable access and mocking `RuntimeService` and `TaskService`.
- Builder to create Mockito-based behaviour of `RuntimeService` accessing variables.
- Builder to create Mockito-based behaviour of `TaskServiceService` accessing variables.
-
+- Verifier to check correct access to variables in `RuntimeService`
+- Verifier to check correct access to variables in `TaskService`
diff --git a/docs/src/orchid/resources/wiki/user-guide/further-outlook.ad b/docs/src/orchid/resources/wiki/user-guide/further-outlook.ad
index e043ef5b..db2a2006 100644
--- a/docs/src/orchid/resources/wiki/user-guide/further-outlook.ad
+++ b/docs/src/orchid/resources/wiki/user-guide/further-outlook.ad
@@ -6,9 +6,6 @@ title: Further outlook
== {{ page.title }}
-* Implement Contracts to be able to check a guards automatically
-* Implement an Anti-Corruption-Layer to protect Message correlation and External Task completion from
- setting wrong values (unguarded).
-
+* Implement Contracts to be able to check guards automatically
// We implemented all features we could imagine so far, if you are missing something, please
// open an issue in https://github.com/holunda-io/camunda-bpm-data/issues[Github].
diff --git a/docs/src/orchid/resources/wiki/user-guide/motivation.ad b/docs/src/orchid/resources/wiki/user-guide/motivation.ad
index a2f82ace..8595cfe1 100644
--- a/docs/src/orchid/resources/wiki/user-guide/motivation.ad
+++ b/docs/src/orchid/resources/wiki/user-guide/motivation.ad
@@ -52,7 +52,7 @@ will throw a `GuardViolationException` if the condition is not met.
import static io.holunda.camunda.bpm.data.guard.CamundaBpmDataGuards.exists;
@Component
-class MyGuardListener extends AbstractGuardTaskListener {
+class MyGuardListener extends DefaultGuardTaskListener {
public MyGuardListener() {
super(newArrayList(exists(ORDER_APPROVED)), true);
@@ -60,3 +60,71 @@ class MyGuardListener extends AbstractGuardTaskListener {
}
----
+=== Anti-Corruption-Layer
+
+If a process is signalled or hit by a correlated message, there is no way to check if the transported variables are set correctly.
+In addition, the variables are written directly to the execution of the correlated process instance. In case of a multi-instance
+event-base sub-process this will eventually overwrite the values of the main execution.
+
+To prevent all this, a feature called Anti-Corruption-Layer (ACL) is implemented. An ACL is there to protect the execution
+from bad modifications and influence the way, the modification is executed. For the protection, an ACL relies on a Variables Guards,
+defining conditions to be satisfied. For the influencing of modification, the `VariableMapTransformer` can be used.
+
+To use the ACL layer you will need to change the way you correlate messages (or signal the execution). Instead of supplying the variables
+directly to the `correlate` method of the `RuntimeService`, the client is wrapping all variables into a map hold by a single transient variable
+and correlate this variable with the process (we call this procedure variable wrapping). On the process side, an execution listener placed
+on the end of the catch event is responsible to extract the variable map from the transient variable, check it by passing through the `VariablesGuard`
+and finally pass over to the `VariableMapTransformer` to map from external to internal representation.
+
+Here is the code, required on the client side to correlate the message.
+
+[source,java]
+----
+@Component
+class SomeService {
+
+ private static AntiCorruptionLayer MY_ACL = CamundaBpmDataACL.guardTransformingReplace(
+ "__transient", // name of the transient variable for wrapping
+ true, // if passes the guard, write to local scope
+ new VariablesGuard(exists(ORDER_ID)), // guard defining condition on ORDER_ID
+ IdentityVariableMapTransformer.INSTANCE // use 1:1 transformer
+ // write the variables without modifications
+ );
+
+ public void correlate() {
+ VariableMap variables = CamundaBpmData.builder()
+ .set(ORDER_ID, "4711")
+ .set(ORDER_APPROVED, false)
+ .build();
+ runtimeService.correlateMessage("message_1", MESSAGE_ACL.checkAndWrap(variables));
+ }
+}
+----
+
+On the process side, the BPMN message catch event should have an `End` listener responsible for unwrapping the values. If the listener is
+implemented as a Spring Bean bounded via delegate expression `${messageAclListener}` then the following code is responsible for providing such a listener:
+
+[source,java]
+----
+@Configuration
+class SomeConfiguration {
+
+ private static AntiCorruptionLayer MY_ACL = CamundaBpmDataACL.guardTransformingReplace(
+ "__transient", // name of the transient variable for wrapping
+ true, // if passes the guard, write to local scope
+ new VariablesGuard(exists(ORDER_ID)), // guard defining condition on ORDER_ID
+ IdentityVariableMapTransformer.INSTANCE // use 1:1 transformer
+ // write the variables without modifications
+ );
+
+ @Bean("messageAclListener")
+ public ExecutionListener messageAclListener() {
+ return MY_ACL.getExecutionListener();
+ }
+}
+----
+
+Such a setup will only allow to correlate messages, if the variables provided include a value for the `ORDER_ID`. It will write all
+variables provided (`ORDER_ID` and `ORDER_APPROVED`) into a local scope of the execution.
+
+
diff --git a/example/example-java/pom.xml b/example/example-java/pom.xml
index 5168d2cf..39e8a099 100644
--- a/example/example-java/pom.xml
+++ b/example/example-java/pom.xml
@@ -6,38 +6,98 @@
io.holunda.data.example
camunda-bpm-data-example-parent
- 0.0.6
+ 1.0.0
camunda-bpm-data-example-java
${project.artifactId}
- jar
- false
+ true
-
io.holunda.data
camunda-bpm-data
+
+ io.holunda.data.example
+ camunda-bpm-data-spin-type-detector
+ ${project.version}
+
+
io.toolisticon.springboot
springboot-swagger-starter
- 0.0.4
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.camunda.bpm.springboot
+ camunda-bpm-spring-boot-starter-rest
+
+
+ org.camunda.bpm
+ camunda-engine-plugin-spin
+
+
+ org.camunda.spin
+ camunda-spin-core
+
+
+ org.camunda.spin
+ camunda-spin-dataformat-json-jackson
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ com.fasterxml.jackson.module
+ jackson-module-kotlin
+
+
+
+ org.codehaus.groovy
+ groovy-all
+
+
+ com.h2database
+ h2
+
+
+
io.holunda.data
- camunda-bpm-data-mockito
+ camunda-bpm-data-test
test
+
+ org.camunda.bpm.assert
+ camunda-bpm-assert
+ test
+
+
org.camunda.bpm.springboot
camunda-bpm-spring-boot-starter-test
- ${camunda-spring-boot.version}
test
@@ -45,6 +105,24 @@
camunda-bpm-mockito
test
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ io.holunda.testing
+ camunda-bpm-jgiven
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/CamundaBpmDataProcessApplication.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/CamundaBpmDataProcessApplication.java
index 06e62984..7bec9797 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/CamundaBpmDataProcessApplication.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/CamundaBpmDataProcessApplication.java
@@ -17,7 +17,7 @@ public class CamundaBpmDataProcessApplication {
@EventListener
public void onDeploy(PostDeployEvent event) {
- orderApprovalInstanceFactory.start();
+ orderApprovalInstanceFactory.start("1");
}
public static void main(String[] args) {
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/Order.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/Order.java
index f6efb62f..092cdb45 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/Order.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/Order.java
@@ -1,43 +1,89 @@
package io.holunda.camunda.bpm.data.example.domain;
+import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+/**
+ * Order business entity.
+ */
public class Order {
+ /**
+ * Id of the order.
+ */
private String orderId;
+ /**
+ * Order create date.
+ */
private Date created;
+ /**
+ * List of order positions.
+ */
private List positions;
+ /**
+ * Constructor.
+ */
public Order() {
-
+ this.positions = new ArrayList<>();
}
+ /**
+ * Constructor to pass all member attributes.
+ * @param orderId order id
+ * @param created creation date
+ * @param positions list of positions.
+ */
public Order(String orderId, Date created, List positions) {
this.orderId = orderId;
this.created = created;
this.positions = positions;
}
+ /**
+ * Sets the order id.
+ * @param orderId order id to set.
+ */
public void setOrderId(String orderId) {
this.orderId = orderId;
}
+ /**
+ * Sets the created date.
+ * @param created created date to set.
+ */
public void setCreated(Date created) {
this.created = created;
}
+ /**
+ * Sets the order positions.
+ * @param positions list of positions to set.
+ */
public void setPositions(List positions) {
this.positions = positions;
}
+ /**
+ * Retrieves the order id.
+ * @return order id.
+ */
public String getOrderId() {
return orderId;
}
+ /**
+ * Retrieves the created date.
+ * @return date of create.
+ */
public Date getCreated() {
return created;
}
+ /**
+ * Retrieves the list of positions.
+ * @return list of positions.
+ */
public List getPositions() {
return positions;
}
@@ -69,4 +115,13 @@ public int hashCode() {
result = 31 * result + positions.hashCode();
return result;
}
+
+ @Override
+ public String toString() {
+ return "Order{" +
+ "orderId='" + orderId + '\'' +
+ ", created=" + created +
+ ", positions=" + positions +
+ '}';
+ }
}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/OrderPosition.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/OrderPosition.java
index 0000c466..b9e680e8 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/OrderPosition.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/OrderPosition.java
@@ -2,41 +2,86 @@
import java.math.BigDecimal;
+/**
+ * Represents order position business entity.
+ */
public class OrderPosition {
+ /**
+ * Position title.
+ */
private String title;
+ /**
+ * Position net cost per unit.
+ */
private BigDecimal netCost;
+ /**
+ * amount of units.
+ */
private Long amount;
+ /**
+ * Empty constructor.
+ */
public OrderPosition() {
}
+ /**
+ * Constructor setting fields.
+ * @param title title of position.
+ * @param netCost net cost per unit.
+ * @param amount amount of units.
+ */
public OrderPosition(String title, BigDecimal netCost, Long amount) {
this.title = title;
this.netCost = netCost;
this.amount = amount;
}
+ /**
+ * Retrieves title.
+ * @return position title.
+ */
public String getTitle() {
return title;
}
+ /**
+ * Retrieves net cost per unit.
+ * @return net cost per unit.
+ */
public BigDecimal getNetCost() {
return netCost;
}
+ /**
+ * Retrieves amount of units.
+ * @return amount of units.
+ */
public Long getAmount() {
return amount;
}
+ /**
+ * Sets the title.
+ * @param title title to set.
+ */
public void setTitle(String title) {
this.title = title;
}
+ /**
+ * Sets net cost per unit.
+ * @param netCost net cost to set.
+ */
public void setNetCost(BigDecimal netCost) {
this.netCost = netCost;
}
+ /**
+ * Sets amount of units.
+ * @param amount amount to set.
+ */
public void setAmount(Long amount) {
this.amount = amount;
}
@@ -68,4 +113,13 @@ public int hashCode() {
result = 31 * result + amount.hashCode();
return result;
}
+
+ @Override
+ public String toString() {
+ return "OrderPosition{" +
+ "title='" + title + '\'' +
+ ", netCost=" + netCost +
+ ", amount=" + amount +
+ '}';
+ }
}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/OrderRepository.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/OrderRepository.java
new file mode 100644
index 00000000..df51c35a
--- /dev/null
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/domain/OrderRepository.java
@@ -0,0 +1,41 @@
+package io.holunda.camunda.bpm.data.example.domain;
+
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Repository to load orders.
+ */
+@Component
+public class OrderRepository {
+
+ /**
+ * Internal representation for storage.
+ */
+ private final Map orders = new HashMap<>();
+
+ public OrderRepository() {
+ final List positions = new ArrayList<>();
+ positions.add(new OrderPosition("Pencil", BigDecimal.valueOf(1.50), 2L));
+ positions.add(new OrderPosition("Pen", BigDecimal.valueOf(2.10), 2L));
+ orders.put("1", new Order("1", Date.from(Instant.now()), positions));
+ }
+
+
+ /**
+ * Loads order by id.
+ * @param orderId order id to load the order for.
+ * @return order or null.
+ */
+ public Order loadOrder(String orderId) {
+ return orders.get(orderId);
+ }
+
+}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApproval.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApproval.java
index 54840933..76d2ab77 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApproval.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApproval.java
@@ -2,10 +2,10 @@
import io.holunda.camunda.bpm.data.example.domain.Order;
import io.holunda.camunda.bpm.data.example.domain.OrderPosition;
-import io.holunda.camunda.bpm.data.example.service.OrderRepository;
+import io.holunda.camunda.bpm.data.example.domain.OrderRepository;
import io.holunda.camunda.bpm.data.factory.VariableFactory;
-import io.holunda.camunda.bpm.data.guard.integration.AbstractGuardExecutionListener;
-import io.holunda.camunda.bpm.data.guard.integration.AbstractGuardTaskListener;
+import io.holunda.camunda.bpm.data.guard.integration.DefaultGuardExecutionListener;
+import io.holunda.camunda.bpm.data.guard.integration.DefaultGuardTaskListener;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.DelegateTask;
import org.camunda.bpm.engine.delegate.ExecutionListener;
@@ -21,94 +21,138 @@
import java.math.BigDecimal;
import static com.google.common.collect.Lists.newArrayList;
-import static io.holunda.camunda.bpm.data.CamundaBpmData.stringVariable;
-import static io.holunda.camunda.bpm.data.CamundaBpmData.customVariable;
-import static io.holunda.camunda.bpm.data.CamundaBpmData.booleanVariable;
+import static io.holunda.camunda.bpm.data.CamundaBpmData.*;
import static io.holunda.camunda.bpm.data.guard.CamundaBpmDataGuards.exists;
+/**
+ * Process backing bean.
+ */
@Configuration
public class OrderApproval {
- public static final String KEY = "order-approval";
-
- public static final VariableFactory ORDER_ID = stringVariable("orderId");
- public static final VariableFactory ORDER = customVariable("order", Order.class);
- public static final VariableFactory ORDER_APPROVED = booleanVariable("orderApproved");
- public static final VariableFactory ORDER_POSITION = customVariable("orderPosition", OrderPosition.class);
- public static final VariableFactory ORDER_TOTAL = customVariable("orderTotal", BigDecimal.class);
-
- private static final Logger logger = LoggerFactory.getLogger(OrderApproval.class);
-
- @Autowired
- private OrderRepository orderRepository;
-
- /**
- * Load a primitive variable by id (string) and store a complex variable (order).
- */
- @Bean
- public JavaDelegate loadOrder() {
- return execution -> {
- String orderId = ORDER_ID.from(execution).get();
- Order order = orderRepository.loadOrder(orderId);
- ORDER.on(execution).set(order);
- };
- }
-
- @Bean
- public ExecutionListener guardExecutionListener() {
- return new AbstractGuardExecutionListener(newArrayList(exists(ORDER_ID)), true) {};
- }
-
- @Bean
- public TaskListener guardTaskListener() {
- return new AbstractGuardTaskListener(newArrayList(exists(ORDER_APPROVED)), true) {};
- }
-
- /**
- * Load a local order position, write a local variable.
- */
- @Bean
- public JavaDelegate calculateOrderPositions() {
- return execution -> {
- OrderPosition orderPosition = ORDER_POSITION.from(execution).get();
- BigDecimal oldTotal = ORDER_TOTAL.from(execution).getOptional().orElse(BigDecimal.ZERO);
- BigDecimal newTotal = oldTotal.add(orderPosition.getNetCost().multiply(BigDecimal.valueOf(orderPosition.getAmount())));
- ORDER_TOTAL.on(execution).setLocal(newTotal);
-
- // alternative
- // ORDER_TOTAL.on(execution).updateLocal(amount -> amount.add(orderPosition.getNetCost().multiply(BigDecimal.valueOf(orderPosition.getAmount()))));
- };
- }
-
-
- /**
- * Read a local variable and store it in global variable.
- */
- @Bean
- public ExecutionListener writeOrderTotal() {
- return execution ->
- {
- BigDecimal total = ORDER_TOTAL.from(execution).get();
- ORDER_TOTAL.on(execution).set(total);
- };
- }
-
- /**
- * Log the task id.
- */
- @EventListener(condition = "#task != null && #task.eventName == 'create'")
- public void taskLogger(DelegateTask task) {
- logger.info("TASK LOGGER: Created user task {}", task.getId());
- }
-
- @EventListener(condition = "#execution != null && #execution.eventName == 'start' && #execution.currentActivityId == 'start_order_created'")
- public void processStartLogger(DelegateExecution execution) {
- logger.info("INSTANCE LOGGER: Started process instance {}", execution.getProcessInstanceId());
- }
-
- @EventListener(condition = "#execution != null && #execution.eventName == 'end' && #execution.currentActivityId == 'end_order_approved'")
- public void processEndLogger(DelegateExecution execution) {
- logger.info("INSTANCE LOGGER: Finished process instance {}", execution.getProcessInstanceId());
- }
+ public static final String KEY = "order-approval";
+
+ enum Elements {
+ start_order_created,
+ user_approve_order,
+ end_order_approved,
+ end_order_rejected;
+
+ static String element(Elements element) {
+ return element.name();
+ }
+ }
+
+ public static final VariableFactory ORDER_ID = stringVariable("orderId");
+ public static final VariableFactory ORDER = customVariable("order", Order.class);
+ public static final VariableFactory ORDER_APPROVED = booleanVariable("orderApproved");
+ public static final VariableFactory ORDER_POSITION = customVariable("orderPosition", OrderPosition.class);
+ public static final VariableFactory ORDER_TOTAL = customVariable("orderTotal", BigDecimal.class);
+
+ private static final Logger logger = LoggerFactory.getLogger(OrderApproval.class);
+
+ @Autowired
+ private OrderRepository orderRepository;
+
+ /**
+ * Loads a primitive variable by id (string) and store a complex variable (order).
+ * Used in "Load Order" service task in BPMN ${loadOrder}
+ *
+ * @return Java delegate
+ */
+ @Bean
+ public JavaDelegate loadOrder() {
+ return execution -> {
+ String orderId = ORDER_ID.from(execution).get();
+ Order order = orderRepository.loadOrder(orderId);
+ ORDER.on(execution).set(order);
+ };
+ }
+
+ /**
+ * Load a local order position, write a local variable.
+ */
+ @Bean
+ public JavaDelegate calculateOrderPositions() {
+ return execution -> {
+ OrderPosition orderPosition = ORDER_POSITION.from(execution).get();
+ BigDecimal oldTotal = ORDER_TOTAL.from(execution).getOptional().orElse(BigDecimal.ZERO);
+ BigDecimal newTotal = oldTotal.add(orderPosition.getNetCost().multiply(BigDecimal.valueOf(orderPosition.getAmount())));
+ ORDER_TOTAL.on(execution).setLocal(newTotal);
+
+ // alternative
+ // ORDER_TOTAL.on(execution).updateLocal(amount -> amount.add(orderPosition.getNetCost().multiply(BigDecimal.valueOf(orderPosition.getAmount()))));
+ };
+ }
+
+
+ /**
+ * Read a local variable and store it in global variable.
+ */
+ @Bean
+ public ExecutionListener writeOrderTotal() {
+ return execution ->
+ {
+ BigDecimal total = ORDER_TOTAL.from(execution).get();
+ ORDER_TOTAL.on(execution).set(total);
+ };
+ }
+
+ /**
+ * Checks that the variable "orderId" exists.
+ * Used as execution listener on start event in BPMN ${guardExecutionListener}
+ *
+ * @return execution listener.
+ */
+ @Bean
+ public ExecutionListener guardExecutionListener() {
+ return new DefaultGuardExecutionListener(newArrayList(exists(ORDER_ID)), true);
+ }
+
+ /**
+ * Checks that the variable "orderApproved" exists.
+ * Used as task listener on complete of user task in BPMN ${taskExecutionListener}
+ *
+ * @return task listener.
+ */
+ @Bean
+ public TaskListener guardTaskListener() {
+ return new DefaultGuardTaskListener(
+ newArrayList(
+ exists(ORDER_APPROVED)
+ ), true
+ );
+ }
+
+
+ /**
+ * Logs the task creation.
+ *
+ * @param task task passed by the engine.
+ */
+ @EventListener(condition = "#task != null && #task.eventName == 'create'")
+ public void taskLogger(DelegateTask task) {
+ logger.info("TASK LOGGER: Created user task {}", task.getId());
+ }
+
+ /**
+ * Logs process start.
+ *
+ * @param execution execution passed by the engine.
+ */
+ @EventListener(condition = "#execution != null && #execution.eventName == 'start' && #execution.currentActivityId == 'start_order_created'")
+ public void processStartLogger(DelegateExecution execution) {
+ logger.info("INSTANCE LOGGER: Started process instance {}", execution.getProcessInstanceId());
+ }
+
+ /**
+ * Logs process end.
+ *
+ * @param execution execution passed by the engine.
+ */
+ @EventListener(condition = "#execution != null && #execution.eventName == 'end' && #execution.currentActivityId == 'end_order_approved'")
+ public void processEndLogger(DelegateExecution execution) {
+ logger.info("INSTANCE LOGGER: Finished process instance {}", execution.getProcessInstanceId());
+ }
}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstance.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstance.java
index c72cf7a0..6ff1aa6f 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstance.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstance.java
@@ -2,15 +2,31 @@
import org.camunda.bpm.engine.runtime.ProcessInstance;
-public class OrderApprovalInstance {
+import java.util.function.Supplier;
+/**
+ * Order Approval process instance supplier.
+ */
+public class OrderApprovalInstance implements Supplier {
+
+ /**
+ * Underlying instance.
+ */
private final ProcessInstance instance;
+ /**
+ * Creates the supplier.
+ * @param instance instance.
+ */
public OrderApprovalInstance(ProcessInstance instance) {
this.instance = instance;
}
- public ProcessInstance getInstance() {
+ /**
+ * Retrieval of the instance.
+ * @return instance.
+ */
+ public ProcessInstance get() {
return instance;
}
}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstanceFactory.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstanceFactory.java
index 66a84f97..90b5b95d 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstanceFactory.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalInstanceFactory.java
@@ -10,18 +10,33 @@
import static io.holunda.camunda.bpm.data.example.process.OrderApproval.ORDER_ID;
import static org.camunda.bpm.engine.variable.Variables.createVariables;
+/**
+ * Factory to create instance factory.
+ */
@Component
public class OrderApprovalInstanceFactory {
+ /**
+ * Runtime service to access Camunda API.
+ */
private final RuntimeService runtimeService;
+ /**
+ * Constructs the factory.
+ * @param runtimeService runtime service to use.
+ */
public OrderApprovalInstanceFactory(RuntimeService runtimeService) {
this.runtimeService = runtimeService;
}
- public OrderApprovalInstance start() {
+ /**
+ * Start new approval process.
+ * @param orderId id of an order.
+ * @return instance supplier.
+ */
+ public OrderApprovalInstance start(String orderId) {
VariableMap vars = createVariables();
- ORDER_ID.on(vars).set("1");
+ ORDER_ID.on(vars).set(orderId);
ProcessInstance instance = runtimeService.startProcessInstanceByKey(OrderApproval.KEY, "order-" + UUID.randomUUID().toString(), vars);
return new OrderApprovalInstance(instance);
}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskController.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskController.java
index d8046f5d..e02ce445 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskController.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskController.java
@@ -1,6 +1,5 @@
package io.holunda.camunda.bpm.data.example.rest;
-
import io.holunda.camunda.bpm.data.example.domain.Order;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.variable.VariableMap;
@@ -12,31 +11,34 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import static io.holunda.camunda.bpm.data.example.process.OrderApproval.ORDER;
-import static io.holunda.camunda.bpm.data.example.process.OrderApproval.ORDER_APPROVED;
-import static org.camunda.bpm.engine.variable.Variables.createVariables;
+import java.math.BigDecimal;
+
+import static io.holunda.camunda.bpm.data.CamundaBpmData.builder;
+import static io.holunda.camunda.bpm.data.example.process.OrderApproval.*;
@RestController
@RequestMapping("/task/approve-order")
public class ApproveOrderTaskController {
- private final TaskService taskService;
- public ApproveOrderTaskController(TaskService taskService) {
- this.taskService = taskService;
- }
-
- @GetMapping("/{taskId}")
- public ResponseEntity loadTask(@PathVariable("taskId") String taskId) {
- Order order = ORDER.from(taskService, taskId).get();
- return ResponseEntity.ok(new ApproveTaskDto(order));
- }
-
- @PostMapping("/{taskId}")
- public ResponseEntity completeTask(@PathVariable("taskId") String taskId, @RequestBody ApproveTaskCompleteDto approveTaskComplete) {
- VariableMap vars = createVariables();
- ORDER_APPROVED.on(vars).set(approveTaskComplete.getApproved());
- taskService.complete(taskId, vars);
- return ResponseEntity.noContent().build();
- }
-
+ private final TaskService taskService;
+
+ public ApproveOrderTaskController(TaskService taskService) {
+ this.taskService = taskService;
+ }
+
+ @GetMapping("/{taskId}")
+ public ResponseEntity loadTask(@PathVariable("taskId") String taskId) {
+ final Order order = ORDER.from(taskService, taskId).get();
+ final BigDecimal orderTotal = ORDER_TOTAL.from(taskService, taskId).get();
+ return ResponseEntity.ok(new ApproveTaskDto(order, orderTotal));
+ }
+
+ @PostMapping("/{taskId}")
+ public ResponseEntity completeTask(@PathVariable("taskId") String taskId, @RequestBody ApproveTaskCompleteDto userInput) {
+ VariableMap vars = builder()
+ .set(ORDER_APPROVED, userInput.getApproved())
+ .build();
+ taskService.complete(taskId, vars);
+ return ResponseEntity.noContent().build();
+ }
}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskCompleteDto.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskCompleteDto.java
index 4890f946..25df939a 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskCompleteDto.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskCompleteDto.java
@@ -1,18 +1,37 @@
package io.holunda.camunda.bpm.data.example.rest;
+/**
+ * DTO to carry the approve task response.
+ */
public class ApproveTaskCompleteDto {
+ /**
+ * Response value.
+ */
private Boolean approved;
- public ApproveTaskCompleteDto() {
-
- }
+ /**
+ * Empty constructor.
+ */
+ public ApproveTaskCompleteDto() { }
+ /**
+ * Constructs DTO with response.
+ * @param approved response value.
+ */
public ApproveTaskCompleteDto(Boolean approved) {this.approved = approved;}
+ /**
+ * Retrieves response value.
+ * @return response value.
+ */
public Boolean getApproved() {
return approved;
}
+ /**
+ * Sets response value.
+ * @param approved response value to set.
+ */
public void setApproved(Boolean approved) {
this.approved = approved;
}
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskDto.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskDto.java
index 07a21fe0..0034742b 100644
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskDto.java
+++ b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/rest/ApproveTaskDto.java
@@ -2,31 +2,70 @@
import io.holunda.camunda.bpm.data.example.domain.Order;
-import java.util.Objects;
+import java.math.BigDecimal;
+/**
+ * Simple DTO carrying the order and the total.
+ */
public class ApproveTaskDto {
+ /**
+ * Order to carry.
+ */
private Order order;
- public ApproveTaskDto() {
+ /**
+ * Order total.
+ */
+ private BigDecimal orderTotal;
- }
+ /**
+ * Empty constructor.
+ */
+ public ApproveTaskDto() { }
- public ApproveTaskDto(Order order) {this.order = order;}
+ /**
+ * Constructs the DTO with order.
+ *
+ * @param order order to store.
+ */
+ public ApproveTaskDto(Order order, BigDecimal orderTotal) {
+ this.order = order;
+ this.orderTotal = orderTotal;
+ }
+ /**
+ * Sets order.
+ *
+ * @param order order to set.
+ */
public void setOrder(Order order) {
this.order = order;
}
+ /**
+ * Sets order total.
+ * @param orderTotal order total.
+ */
+ public void setOrderTotal(BigDecimal orderTotal) {
+ this.orderTotal = orderTotal;
+ }
+
+ /**
+ * Get order.
+ *
+ * @return order to get.
+ */
public Order getOrder() {
return order;
}
- @Override
- public String toString() {
- return "ApproveTaskDto{" +
- "order=" + order +
- '}';
+ /**
+ * Retrieves the total.
+ * @return order total.
+ */
+ public BigDecimal getOrderTotal() {
+ return orderTotal;
}
@Override
@@ -39,11 +78,26 @@ public boolean equals(Object o) {
}
ApproveTaskDto that = (ApproveTaskDto) o;
- return Objects.equals(order, that.order);
+
+ if (order != null ? !order.equals(that.order) : that.order != null) {
+ return false;
+ }
+ return orderTotal != null ? orderTotal.equals(that.orderTotal) : that.orderTotal == null;
}
@Override
public int hashCode() {
- return order != null ? order.hashCode() : 0;
+ int result = order != null ? order.hashCode() : 0;
+ result = 31 * result + (orderTotal != null ? orderTotal.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ApproveTaskDto{" +
+ "order=" + order +
+ ", orderTotal=" + orderTotal +
+ '}';
}
}
+
diff --git a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/service/OrderRepository.java b/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/service/OrderRepository.java
deleted file mode 100644
index 649a0182..00000000
--- a/example/example-java/src/main/java/io/holunda/camunda/bpm/data/example/service/OrderRepository.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package io.holunda.camunda.bpm.data.example.service;
-
-import io.holunda.camunda.bpm.data.example.domain.Order;
-import io.holunda.camunda.bpm.data.example.domain.OrderPosition;
-import org.springframework.stereotype.Component;
-
-import java.math.BigDecimal;
-import java.sql.Date;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@Component
-public class OrderRepository {
-
- private final Map orders = new HashMap<>();
-
- public OrderRepository() {
- List positions = new ArrayList<>();
- positions.add(new OrderPosition("Pencil", BigDecimal.valueOf(1.50), 2L));
- positions.add(new OrderPosition("Pen", BigDecimal.valueOf(2.10), 2L));
- orders.put("1", new Order("1", Date.from(Instant.now()), positions));
- }
-
-
- public Order loadOrder(String orderId) {
- return orders.get(orderId);
- }
-
-}
diff --git a/example/example-java/src/main/resources/application.yaml b/example/example-java/src/main/resources/application.yaml
index cdf71e8f..0f952828 100644
--- a/example/example-java/src/main/resources/application.yaml
+++ b/example/example-java/src/main/resources/application.yaml
@@ -7,6 +7,7 @@ spring:
open-in-view: true
camunda:
bpm:
+ default-serialization-format: application/json
admin-user:
id: admin
email: admin@localhost
diff --git a/example/example-java/src/main/resources/order_approval.bpmn b/example/example-java/src/main/resources/order_approval.bpmn
index 6b308cd0..69521f1c 100644
--- a/example/example-java/src/main/resources/order_approval.bpmn
+++ b/example/example-java/src/main/resources/order_approval.bpmn
@@ -13,20 +13,20 @@
SequenceFlow_0yomyfk
SequenceFlow_1bg5dcq
+
+
+ DataObjectReference_0tq2vmr
+ Property_1fo4y4m
+
+
+ DataObjectReference_0843zr5
+
-
-
-
- ${!orderApproved}
-
-
+
SequenceFlow_0bu7jj9
xor_approved_yes
xor_approved_no
-
-
-
Please approve order ${order.orderId}.
@@ -37,6 +37,14 @@
SequenceFlow_19r0xpo
SequenceFlow_0bu7jj9
+
+
+ DataObjectReference_0o9907t
+ Property_0a3extx
+
+
+ DataObjectReference_10cvpxg
+
@@ -44,6 +52,21 @@
SequenceFlow_1bg5dcq
SequenceFlow_19r0xpo
+
+
+ DataObjectReference_0843zr5
+ Property_1wyqibd
+
+
+ DataObjectReference_08nw8lv
+ Property_1wyqibd
+
+
+ DataObjectReference_08nw8lv
+
+
+ DataObjectReference_0o9907t
+
@@ -52,75 +75,160 @@
xor_approved_no
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${!orderApproved}
+
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/process/GuardProcessTest.java b/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/process/GuardProcessTest.java
index 27f376e2..95eb964e 100644
--- a/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/process/GuardProcessTest.java
+++ b/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/process/GuardProcessTest.java
@@ -2,6 +2,8 @@
import com.google.common.collect.Lists;
import io.holunda.camunda.bpm.data.example.domain.Order;
+import io.holunda.camunda.bpm.data.guard.integration.GuardViolationException;
+import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.camunda.bpm.engine.runtime.Job;
import org.camunda.bpm.engine.task.Task;
@@ -13,13 +15,12 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import java.sql.Date;
import java.time.Instant;
import java.util.ArrayList;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
@Deployment(resources = "order_approval.bpmn")
@@ -28,8 +29,6 @@ public class GuardProcessTest {
public final ProcessEngineRule rule = new StandaloneInMemoryTestConfiguration(
Lists.newArrayList(new SpinProcessEnginePlugin())
).rule();
- @Rule
- public ExpectedException thrown = ExpectedException.none();
@Before
public void register() {
@@ -54,33 +53,30 @@ public JavaDelegate calculateOrderPositions() {
@Test
public void shouldFireExceptionIfOrderIdIsMissing() {
- thrown.expectMessage("Guard violated by execution '6' in activity 'Order created'");
+ assertThrows(
+ GuardViolationException.class,
+ // manual start by-passing the factory
+ () -> rule.getRuntimeService().startProcessInstanceByKey(OrderApproval.KEY),
+ "Guard violated by execution '6' in activity 'Order created'\nExpecting variable 'orderId' to be set, but it was not found.\n");
- // manual start by-passing the factory
- rule.getRuntimeService().startProcessInstanceByKey(OrderApproval.KEY);
- fail("Should not get here");
}
@Test
public void shouldFireExceptionApproveDecisionIsMissing() {
- thrown.expectMessage("Guard violated in task 'Approve order' (taskId: '21')");
-
OrderApprovalInstanceFactory factory = new OrderApprovalInstanceFactory(rule.getRuntimeService());
- factory.start();
+ factory.start("1");
// async after start
Job asyncStart = rule.getManagementService().createJobQuery().singleResult();
rule.getManagementService().executeJob(asyncStart.getId());
-
Task task = rule.getTaskService().createTaskQuery().singleResult();
- rule.getTaskService().complete(task.getId());
-
- // async after user task
- Job job = rule.getManagementService().createJobQuery().singleResult();
- rule.getManagementService().executeJob(job.getId());
- fail("Should not get here");
+ assertThrows(
+ ProcessEngineException.class,
+ () -> rule.getTaskService().complete(task.getId()),
+ "Guard violated in task 'Approve order' (taskId: '21')\nExpecting variable 'orderApproved' to be set, but it was not found.\n"
+ );
}
}
diff --git a/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalProcessTest.java b/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalProcessTest.java
new file mode 100644
index 00000000..a4d70126
--- /dev/null
+++ b/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/process/OrderApprovalProcessTest.java
@@ -0,0 +1,132 @@
+package io.holunda.camunda.bpm.data.example.process;
+
+
+import com.google.common.collect.Lists;
+import io.holunda.camunda.bpm.data.builder.VariableMapBuilder;
+import io.holunda.camunda.bpm.data.example.domain.Order;
+import io.holunda.camunda.bpm.data.example.domain.OrderPosition;
+import org.camunda.bpm.engine.delegate.JavaDelegate;
+import org.camunda.bpm.engine.test.Deployment;
+import org.camunda.bpm.engine.test.ProcessEngineRule;
+import org.camunda.bpm.engine.test.mock.Mocks;
+import org.camunda.bpm.spring.boot.starter.test.helper.StandaloneInMemoryTestConfiguration;
+import org.camunda.spin.plugin.impl.SpinProcessEnginePlugin;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.time.Instant;
+
+import static io.holunda.camunda.bpm.data.example.process.OrderApproval.*;
+import static io.holunda.camunda.bpm.data.example.process.OrderApproval.Elements.*;
+import static org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.*;
+import static org.junit.Assert.assertTrue;
+
+@Deployment(resources = "order_approval.bpmn")
+public class OrderApprovalProcessTest {
+
+ @Rule
+ public final ProcessEngineRule rule = new StandaloneInMemoryTestConfiguration(
+ Lists.newArrayList(new SpinProcessEnginePlugin())
+ ).rule();
+
+ private OrderApprovalInstanceFactory factory;
+
+
+ @Before
+ public void register() {
+ factory = new OrderApprovalInstanceFactory(rule.getRuntimeService());
+ OrderApproval config = new OrderApproval();
+ Mocks.register("guardExecutionListener", config.guardExecutionListener());
+ Mocks.register("guardTaskListener", config.guardTaskListener());
+ Mocks.register("orderApproval", new MockOrderApproval());
+ }
+
+ @Test
+ public void shouldDeploy() {
+ // empty method body checks deployment
+ assertTrue(true);
+ }
+
+ @Test
+ public void shouldStartAsync() {
+ OrderApprovalInstance instance = factory.start("1");
+
+ assertThat(instance.get()).isStarted();
+ assertThat(instance.get()).isWaitingAt(element(start_order_created));
+ }
+
+ @Test
+ public void shouldStartAndWaitInUserTask() {
+ OrderApprovalInstance instance = factory.start("1");
+
+ assertThat(instance.get()).isStarted();
+
+ // pass async on start
+ execute(job());
+
+ assertThat(instance.get()).isWaitingAt(element(user_approve_order));
+ }
+
+ @Test
+ public void shouldStartAndWaitInUserTaskAndApprove() {
+ OrderApprovalInstance instance = factory.start("1");
+
+ assertThat(instance.get()).isStarted();
+
+ // pass async on start
+ execute(job());
+
+ // complete user task
+ complete(task(), new VariableMapBuilder().set(ORDER_APPROVED, true).build());
+ // pass async oafter user task
+ execute(job());
+
+ assertThat(instance.get()).isEnded();
+ assertThat(instance.get()).hasPassed(element(end_order_approved));
+ }
+
+ @Test
+ public void shouldStartAndWaitInUserTaskAndReject() {
+ OrderApprovalInstance instance = factory.start("1");
+
+ assertThat(instance.get()).isStarted();
+
+ // pass async on start
+ execute(job());
+
+ // complete user task
+ complete(task(), new VariableMapBuilder().set(ORDER_APPROVED, false).build());
+ // pass async oafter user task
+ execute(job());
+
+ assertThat(instance.get()).isEnded();
+ assertThat(instance.get()).hasPassed(element(end_order_rejected));
+ }
+
+
+ /**
+ * Stub for the test.
+ */
+ static class MockOrderApproval {
+ public JavaDelegate loadOrder() {
+ return execution -> {
+ ORDER.on(execution).set(new Order("1", Date.from(Instant.now()), Lists.newArrayList(
+ new OrderPosition("Pencil", BigDecimal.valueOf(1.99), 3L),
+ new OrderPosition("Sheet", BigDecimal.valueOf(0.17), 3L)
+ )));
+ };
+ }
+
+ public JavaDelegate calculateOrderPositions() {
+ return execution -> {
+ ORDER_TOTAL.on(execution).set(BigDecimal.valueOf(6.48));
+ };
+ }
+
+ public JavaDelegate writeOrderTotal() { return execution -> {}; }
+ }
+
+}
diff --git a/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskControllerTest.java b/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskControllerTest.java
index a903bd6b..a3eac7c9 100644
--- a/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskControllerTest.java
+++ b/example/example-java/src/test/java/io/holunda/camunda/bpm/data/example/rest/ApproveOrderTaskControllerTest.java
@@ -3,60 +3,67 @@
import io.holunda.camunda.bpm.data.example.domain.Order;
import io.holunda.camunda.bpm.data.mockito.TaskServiceMockVerifier;
import org.camunda.bpm.engine.TaskService;
-import org.camunda.bpm.engine.variable.VariableMap;
+import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.UUID;
import static io.holunda.camunda.bpm.data.CamundaBpmData.builder;
-import static io.holunda.camunda.bpm.data.example.process.OrderApproval.ORDER;
-import static io.holunda.camunda.bpm.data.example.process.OrderApproval.ORDER_APPROVED;
+import static io.holunda.camunda.bpm.data.example.process.OrderApproval.*;
import static io.holunda.camunda.bpm.data.mockito.CamundaBpmDataMockito.taskServiceMockVerifier;
import static io.holunda.camunda.bpm.data.mockito.CamundaBpmDataMockito.taskServiceVariableMockBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
/**
* Demonstrates the usage of Task Service Variable Mock Builder and Task Service Verifier.
*/
public class ApproveOrderTaskControllerTest {
- private static Order order = new Order("ORDER-ID-1", new Date(), new ArrayList<>());
- private TaskService taskService = mock(TaskService.class);
- private TaskServiceMockVerifier verifier = taskServiceMockVerifier(taskService);
- private ApproveOrderTaskController controller = new ApproveOrderTaskController(taskService);
+ private final static Order order = new Order("ORDER-ID-1", new Date(), new ArrayList<>());
+ private final TaskService taskService = mock(TaskService.class);
+ private final TaskServiceMockVerifier verifier = taskServiceMockVerifier(taskService);
+ private final ApproveOrderTaskController controller = new ApproveOrderTaskController(taskService);
+ private String taskId;
+
+ @Before
+ public void prepareTest() {
+ reset(taskService);
+ taskId = UUID.randomUUID().toString();
+ }
@Test
public void testLoadTask() {
-
// given
- String taskId = UUID.randomUUID().toString();
- taskServiceVariableMockBuilder(taskService).initial(ORDER, order).build();
+ taskServiceVariableMockBuilder(taskService)
+ .initial(ORDER, order)
+ .initial(ORDER_TOTAL, BigDecimal.ZERO)
+ .build();
// when
ResponseEntity responseEntity = controller.loadTask(taskId);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
- assertThat(responseEntity.getBody()).isEqualTo(new ApproveTaskDto(order));
+ assertThat(responseEntity.getBody()).isEqualTo(new ApproveTaskDto(order, BigDecimal.ZERO));
verifier.verifyGet(ORDER, taskId);
+ verifier.verifyGet(ORDER_TOTAL, taskId);
verifier.verifyNoMoreInteractions();
}
@Test
public void testCompleteTask() {
-
- // given
- String taskId = UUID.randomUUID().toString();
// when
ApproveTaskCompleteDto response = new ApproveTaskCompleteDto(true);
ResponseEntity responseEntity = controller.completeTask(taskId, response);
// then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
- VariableMap variables = builder().set(ORDER_APPROVED, response.getApproved()).build();
- verifier.verifyComplete(variables, taskId);
+ verifier.verifyComplete(builder().set(ORDER_APPROVED, response.getApproved()).build(), taskId);
verifier.verifyNoMoreInteractions();
}
+
}
diff --git a/example/example-kotlin/pom.xml b/example/example-kotlin/pom.xml
index abaa5bed..57b91c13 100644
--- a/example/example-kotlin/pom.xml
+++ b/example/example-kotlin/pom.xml
@@ -6,12 +6,11 @@
io.holunda.data.example
camunda-bpm-data-example-parent
- 0.0.6
+ 1.0.0
camunda-bpm-data-example-kotlin
${project.artifactId}
- jar
false
@@ -23,8 +22,56 @@
camunda-bpm-data
- io.holunda.data
- camunda-bpm-data-kotlin
+ io.holunda.data.example
+ camunda-bpm-data-spin-type-detector
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.camunda.bpm.springboot
+ camunda-bpm-spring-boot-starter-rest
+
+
+ org.camunda.bpm
+ camunda-engine-plugin-spin
+
+
+ org.camunda.spin
+ camunda-spin-core
+
+
+ org.camunda.spin
+ camunda-spin-dataformat-json-jackson
+
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ com.fasterxml.jackson.module
+ jackson-module-kotlin
+
+
+
+ org.codehaus.groovy
+ groovy-all
+
+
+ com.h2database
+ h2
@@ -63,15 +110,49 @@
io.toolisticon.springboot
springboot-swagger-starter
- 0.0.4
+
+
+
+ io.holunda.data
+ camunda-bpm-data-test
+ test
+
+
+
+ io.holunda.testing
+ camunda-bpm-jgiven
+
+
+ com.tngtech.jgiven
+ jgiven-junit
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
org.jetbrains.kotlin
kotlin-maven-plugin
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ com.tngtech.jgiven
+ jgiven-maven-plugin
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
diff --git a/example/example-kotlin/src/main/kotlin/CamundaBpmDataKotlinExample.kt b/example/example-kotlin/src/main/kotlin/CamundaBpmDataKotlinExample.kt
index bc6977ff..e8a4b708 100644
--- a/example/example-kotlin/src/main/kotlin/CamundaBpmDataKotlinExample.kt
+++ b/example/example-kotlin/src/main/kotlin/CamundaBpmDataKotlinExample.kt
@@ -23,6 +23,6 @@ class CamundaBpmDataKotlinExampleApplication {
@EventListener
fun onDeploy(event: PostDeployEvent) {
- orderApprovalInstanceFactory.start()
+ orderApprovalInstanceFactory.start("1")
}
}
diff --git a/example/example-kotlin/src/main/kotlin/domain/Order.kt b/example/example-kotlin/src/main/kotlin/domain/Order.kt
index 1fd8e5e0..68e6740d 100644
--- a/example/example-kotlin/src/main/kotlin/domain/Order.kt
+++ b/example/example-kotlin/src/main/kotlin/domain/Order.kt
@@ -2,8 +2,20 @@ package io.holunda.camunda.bpm.data.example.kotlin.domain
import java.util.*
+/**
+ * Order business entity.
+ */
data class Order(
- val orderId: String,
- val created: Date,
- val positions: List = listOf()
+ /**
+ * Order id.
+ */
+ val orderId: String,
+ /**
+ * Order create date.
+ */
+ val created: Date,
+ /**
+ * List of order positions.
+ */
+ val positions: List = listOf()
)
diff --git a/example/example-kotlin/src/main/kotlin/domain/OrderPosition.kt b/example/example-kotlin/src/main/kotlin/domain/OrderPosition.kt
index d2161532..d4e8d74d 100644
--- a/example/example-kotlin/src/main/kotlin/domain/OrderPosition.kt
+++ b/example/example-kotlin/src/main/kotlin/domain/OrderPosition.kt
@@ -2,8 +2,20 @@ package io.holunda.camunda.bpm.data.example.kotlin.domain
import java.math.BigDecimal
+/**
+ * Order position business entity.
+ */
data class OrderPosition(
- val title: String,
- val netCost: BigDecimal,
- val amount: Long
+ /**
+ * Title.
+ */
+ val title: String,
+ /**
+ * Net cost per unit.
+ */
+ val netCost: BigDecimal,
+ /**
+ * Amount (number of units).
+ */
+ val amount: Long
)
diff --git a/example/example-kotlin/src/main/kotlin/domain/OrderRepository.kt b/example/example-kotlin/src/main/kotlin/domain/OrderRepository.kt
new file mode 100644
index 00000000..5bd92d5a
--- /dev/null
+++ b/example/example-kotlin/src/main/kotlin/domain/OrderRepository.kt
@@ -0,0 +1,27 @@
+package io.holunda.camunda.bpm.data.example.kotlin.domain
+
+import org.springframework.stereotype.Component
+import java.math.BigDecimal
+import java.sql.Date
+import java.time.Instant
+
+/**
+ * Repository.
+ */
+@Component
+class OrderRepository {
+
+ val orders = mapOf("1" to Order(
+ orderId = "1",
+ created = Date.from(Instant.now()),
+ positions = listOf(
+ OrderPosition(title = "Pencil", netCost = BigDecimal.valueOf(1.50), amount = 2),
+ OrderPosition(title = "Pen", netCost = BigDecimal.valueOf(2.10), amount = 2)
+ )
+ ))
+
+
+ fun loadOrder(orderId: String): Order {
+ return orders.getValue(orderId)
+ }
+}
diff --git a/example/example-kotlin/src/main/kotlin/process/OrderApproval.kt b/example/example-kotlin/src/main/kotlin/process/OrderApproval.kt
index 5875ae2d..00fbf173 100644
--- a/example/example-kotlin/src/main/kotlin/process/OrderApproval.kt
+++ b/example/example-kotlin/src/main/kotlin/process/OrderApproval.kt
@@ -9,10 +9,8 @@ import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variable
import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variables.ORDER_ID
import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variables.ORDER_POSITION
import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variables.ORDER_TOTAL
-import io.holunda.camunda.bpm.data.example.kotlin.service.OrderRepository
+import io.holunda.camunda.bpm.data.example.kotlin.domain.OrderRepository
import io.holunda.camunda.bpm.data.factory.VariableFactory
-import io.holunda.camunda.bpm.data.remove
-import io.holunda.camunda.bpm.data.set
import mu.KLogging
import org.camunda.bpm.engine.delegate.DelegateExecution
import org.camunda.bpm.engine.delegate.DelegateTask
@@ -24,70 +22,73 @@ import org.springframework.context.annotation.Configuration
import org.springframework.context.event.EventListener
import java.math.BigDecimal
+/**
+ * Backing bean.
+ */
@Configuration
class OrderApproval {
- @Autowired
- lateinit var orderRepository: OrderRepository
+ @Autowired
+ lateinit var orderRepository: OrderRepository
- companion object : KLogging() {
- const val KEY = "order-approval"
- }
+ companion object : KLogging() {
+ const val KEY = "order-approval"
+ }
- object Variables {
- val ORDER_ID = stringVariable("orderId")
- val ORDER: VariableFactory = customVariable("order")
- val ORDER_APPROVED = booleanVariable("orderApproved")
- val ORDER_POSITION: VariableFactory = customVariable("orderPosition")
- val ORDER_TOTAL: VariableFactory = customVariable("orderTotal")
- }
+ object Variables {
+ val ORDER_ID = stringVariable("orderId")
+ val ORDER: VariableFactory = customVariable("order")
+ val ORDER_APPROVED = booleanVariable("orderApproved")
+ val ORDER_POSITION: VariableFactory = customVariable("orderPosition")
+ val ORDER_TOTAL: VariableFactory = customVariable("orderTotal")
+ }
- /**
- * Load a primitive variable by id (string) and store a complex variable (order).
- */
- @Bean
- fun loadOrder() = JavaDelegate { execution ->
- val orderId = ORDER_ID.from(execution).get()
- val order = orderRepository.loadOrder(orderId)
- ORDER.on(execution).set(order)
- ORDER_TOTAL.on(execution).set(BigDecimal.ZERO)
- }
+ /**
+ * Load a primitive variable by id (string) and store a complex variable (order).
+ */
+ @Bean
+ fun loadOrder() = JavaDelegate { execution ->
+ val orderId = ORDER_ID.from(execution).get()
+ val order = orderRepository.loadOrder(orderId)
+ ORDER.on(execution).set(order)
+ ORDER_TOTAL.on(execution).set(BigDecimal.ZERO)
+ }
- /**
- * Load a local order position, write a local variable.
- */
- @Bean
- fun calculateOrderPositions() = JavaDelegate { execution ->
- val orderPosition = ORDER_POSITION.from(execution).get()
+ /**
+ * Load a local order position, write a local variable.
+ */
+ @Bean
+ fun calculateOrderPositions() = JavaDelegate { execution ->
+ val orderPosition = ORDER_POSITION.from(execution).get()
- ORDER_TOTAL.on(execution).update { it.plus(orderPosition.netCost.times(BigDecimal.valueOf(orderPosition.amount))) }
- }
+ ORDER_TOTAL.on(execution).update { it.plus(orderPosition.netCost.times(BigDecimal.valueOf(orderPosition.amount))) }
+ }
- /**
- * Read a local variable and store it in global variable.
- */
- @Bean
- fun writeOrderTotal() = ExecutionListener { execution ->
- val total = ORDER_TOTAL.from(execution).get()
- ORDER_TOTAL.on(execution).set(total)
- }
+ /**
+ * Read a local variable and store it in global variable.
+ */
+ @Bean
+ fun writeOrderTotal() = ExecutionListener { execution ->
+ val total = ORDER_TOTAL.from(execution).get()
+ ORDER_TOTAL.on(execution).set(total)
+ }
- /**
- * Log the task id.
- */
- @EventListener(condition = "#task != null && #task.eventName == 'create'")
- fun taskLogger(task: DelegateTask) {
- logger.info("TASK LOGGER: Created user task ${task.id}")
- }
+ /**
+ * Log the task id.
+ */
+ @EventListener(condition = "#task != null && #task.eventName == 'create'")
+ fun taskLogger(task: DelegateTask) {
+ logger.info("TASK LOGGER: Created user task ${task.id}")
+ }
- @EventListener(condition = "#execution != null && #execution.eventName == 'start' && #execution.currentActivityId == 'start_order_created'")
- fun processStartLogger(execution: DelegateExecution) {
- logger.info { "INSTANCE LOGGER: Started process instance ${execution.processInstanceId}" }
- }
+ @EventListener(condition = "#execution != null && #execution.eventName == 'start' && #execution.currentActivityId == 'start_order_created'")
+ fun processStartLogger(execution: DelegateExecution) {
+ logger.info { "INSTANCE LOGGER: Started process instance ${execution.processInstanceId}" }
+ }
- @EventListener(condition = "#execution != null && #execution.eventName == 'end' && #execution.currentActivityId == 'end_order_approved'")
- fun processEndLogger(execution: DelegateExecution) {
- logger.info { "INSTANCE LOGGER: Finished process instance ${execution.processInstanceId}" }
- }
+ @EventListener(condition = "#execution != null && #execution.eventName == 'end' && #execution.currentActivityId == 'end_order_approved'")
+ fun processEndLogger(execution: DelegateExecution) {
+ logger.info { "INSTANCE LOGGER: Finished process instance ${execution.processInstanceId}" }
+ }
}
diff --git a/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstance.kt b/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstance.kt
index cc270e86..83f78049 100644
--- a/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstance.kt
+++ b/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstance.kt
@@ -1,12 +1,10 @@
package io.holunda.camunda.bpm.data.example.kotlin.process
-import org.camunda.bpm.engine.RuntimeService
-import org.camunda.bpm.engine.TaskService
import org.camunda.bpm.engine.runtime.ProcessInstance
/**
* Represents order delivery process instance.
*/
class OrderApprovalInstance(
- private val delegate: ProcessInstance) : ProcessInstance by delegate {
+ private val delegate: ProcessInstance) : ProcessInstance by delegate {
}
diff --git a/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstanceFactory.kt b/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstanceFactory.kt
index bf09592c..49a90a7a 100644
--- a/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstanceFactory.kt
+++ b/example/example-kotlin/src/main/kotlin/process/OrderApprovalInstanceFactory.kt
@@ -1,24 +1,27 @@
package io.holunda.camunda.bpm.data.example.kotlin.process
-import io.holunda.camunda.bpm.data.builder
+import io.holunda.camunda.bpm.data.builder.VariableMapBuilder
import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variables.ORDER_ID
import org.camunda.bpm.engine.RuntimeService
import org.camunda.bpm.engine.variable.Variables.createVariables
import org.springframework.stereotype.Component
import java.util.*
+/**
+ * Instance factory.
+ */
@Component
class OrderApprovalInstanceFactory(
- private val runtimeService: RuntimeService
+ private val runtimeService: RuntimeService
) {
- fun start(): OrderApprovalInstance {
- val vars = createVariables()
- ORDER_ID.on(vars).set("1")
- val instance = runtimeService.startProcessInstanceByKey(OrderApproval.KEY, "order-${UUID.randomUUID()}", vars)
- return OrderApprovalInstance(instance)
- }
+ /**
+ * Starts the approval process.
+ */
+ fun start(id: String): OrderApprovalInstance {
+ val vars = VariableMapBuilder().set(ORDER_ID, id).build()
+ val instance = runtimeService.startProcessInstanceByKey(OrderApproval.KEY, "order-${UUID.randomUUID()}", vars)
+ return OrderApprovalInstance(instance)
+ }
- fun setVariables(executionId: String) {
- }
}
diff --git a/example/example-kotlin/src/main/kotlin/rest/ApproveOrderTaskController.kt b/example/example-kotlin/src/main/kotlin/rest/ApproveOrderTaskController.kt
index 2106b6de..981f32ea 100644
--- a/example/example-kotlin/src/main/kotlin/rest/ApproveOrderTaskController.kt
+++ b/example/example-kotlin/src/main/kotlin/rest/ApproveOrderTaskController.kt
@@ -3,10 +3,12 @@ package io.holunda.camunda.bpm.data.example.kotlin.rest
import io.holunda.camunda.bpm.data.example.kotlin.domain.Order
import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variables.ORDER
import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variables.ORDER_APPROVED
+import io.holunda.camunda.bpm.data.example.kotlin.process.OrderApproval.Variables.ORDER_TOTAL
import org.camunda.bpm.engine.TaskService
import org.camunda.bpm.engine.variable.Variables.createVariables
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
+import java.math.BigDecimal
@RestController
@RequestMapping("/task/approve-order")
@@ -17,7 +19,8 @@ class ApproveOrderTaskController(
@GetMapping("/{taskId}")
fun loadTask(@PathVariable("taskId") taskId: String): ResponseEntity {
val order = ORDER.from(taskService, taskId).get()
- return ResponseEntity.ok(ApproveTaskDto(order))
+ val orderTotal = ORDER_TOTAL.from(taskService, taskId).get()
+ return ResponseEntity.ok(ApproveTaskDto(order, orderTotal))
}
@PostMapping("/{taskId}")
@@ -29,6 +32,6 @@ class ApproveOrderTaskController(
}
}
-data class ApproveTaskDto(val order: Order)
+data class ApproveTaskDto(val order: Order, val orderTotal: BigDecimal)
data class ApproveTaskCompleteDto(val approved: Boolean)
diff --git a/example/example-kotlin/src/main/kotlin/service/OrderRepository.kt b/example/example-kotlin/src/main/kotlin/service/OrderRepository.kt
deleted file mode 100644
index fc45d7ca..00000000
--- a/example/example-kotlin/src/main/kotlin/service/OrderRepository.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.holunda.camunda.bpm.data.example.kotlin.service
-
-import io.holunda.camunda.bpm.data.example.kotlin.domain.Order
-import io.holunda.camunda.bpm.data.example.kotlin.domain.OrderPosition
-import org.springframework.stereotype.Component
-import java.math.BigDecimal
-import java.sql.Date
-import java.time.Instant
-
-@Component
-class OrderRepository {
-
- val orders = mapOf("1" to Order(
- orderId = "1",
- created = Date.from(Instant.now()),
- positions = listOf(
- OrderPosition(title = "Pencil", netCost = BigDecimal.valueOf(1.50), amount = 2),
- OrderPosition(title = "Pen", netCost = BigDecimal.valueOf(2.10), amount = 2)
- )
- ))
-
-
- fun loadOrder(orderId: String): Order {
- return orders.getValue(orderId)
- }
-}
diff --git a/example/example-kotlin/src/main/kotlin/spin/JacksonDataFormatConfigurator.kt b/example/example-kotlin/src/main/kotlin/spin/JacksonDataFormatConfigurator.kt
deleted file mode 100644
index 025ca64a..00000000
--- a/example/example-kotlin/src/main/kotlin/spin/JacksonDataFormatConfigurator.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.holunda.camunda.bpm.data.example.kotlin.spin
-
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import org.camunda.spin.impl.json.jackson.format.JacksonJsonDataFormat
-import org.camunda.spin.spi.DataFormatConfigurator
-
-class JacksonDataFormatConfigurator : DataFormatConfigurator {
-
- override fun configure(dataFormat: JacksonJsonDataFormat) {
- val objectMapper = dataFormat.objectMapper
- objectMapper.registerModule(KotlinModule())
- objectMapper.registerModule(JavaTimeModule())
- }
-
- override fun getDataFormatClass(): Class = JacksonJsonDataFormat::class.java
-
-}
diff --git a/example/example-kotlin/src/main/kotlin/spin/KotlinJacksonDataFormatConfigurator.kt b/example/example-kotlin/src/main/kotlin/spin/KotlinJacksonDataFormatConfigurator.kt
new file mode 100644
index 00000000..84821e8c
--- /dev/null
+++ b/example/example-kotlin/src/main/kotlin/spin/KotlinJacksonDataFormatConfigurator.kt
@@ -0,0 +1,17 @@
+package io.holunda.camunda.bpm.data.example.kotlin.spin
+
+import com.fasterxml.jackson.databind.SerializationFeature
+import com.fasterxml.jackson.module.kotlin.KotlinModule
+import org.camunda.spin.impl.json.jackson.format.JacksonJsonDataFormat
+import org.camunda.spin.spi.DataFormatConfigurator
+
+class KotlinJacksonDataFormatConfigurator : DataFormatConfigurator {
+
+ override fun configure(dataFormat: JacksonJsonDataFormat) {
+ val objectMapper = dataFormat.objectMapper
+ objectMapper.registerModule(KotlinModule())
+ objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
+ }
+
+ override fun getDataFormatClass(): Class = JacksonJsonDataFormat::class.java
+}
\ No newline at end of file
diff --git a/example/example-kotlin/src/main/resources/META-INF/services/org.camunda.spin.spi.DataFormatConfigurator b/example/example-kotlin/src/main/resources/META-INF/services/org.camunda.spin.spi.DataFormatConfigurator
index 0869c041..f2b7e915 100644
--- a/example/example-kotlin/src/main/resources/META-INF/services/org.camunda.spin.spi.DataFormatConfigurator
+++ b/example/example-kotlin/src/main/resources/META-INF/services/org.camunda.spin.spi.DataFormatConfigurator
@@ -1 +1 @@
-io.holunda.camunda.bpm.data.example.kotlin.spin.JacksonDataFormatConfigurator
+io.holunda.camunda.bpm.data.example.kotlin.spin.KotlinJacksonDataFormatConfigurator
diff --git a/example/example-kotlin/src/main/resources/application.yml b/example/example-kotlin/src/main/resources/application.yml
index 90ad96ad..eb81d18a 100644
--- a/example/example-kotlin/src/main/resources/application.yml
+++ b/example/example-kotlin/src/main/resources/application.yml
@@ -7,6 +7,7 @@ spring:
open-in-view: true
camunda:
bpm:
+ default-serialization-format: application/json
filter:
create: All Tasks
admin-user:
diff --git a/example/example-kotlin/src/test/kotlin/itest/CamundaBpmDataITestBase.kt b/example/example-kotlin/src/test/kotlin/itest/CamundaBpmDataITestBase.kt
index c7ef35e2..d6ad5b42 100644
--- a/example/example-kotlin/src/test/kotlin/itest/CamundaBpmDataITestBase.kt
+++ b/example/example-kotlin/src/test/kotlin/itest/CamundaBpmDataITestBase.kt
@@ -9,11 +9,23 @@ import com.tngtech.jgiven.integration.spring.EnableJGiven
import com.tngtech.jgiven.integration.spring.JGivenStage
import com.tngtech.jgiven.integration.spring.SpringScenarioTest
import io.holunda.camunda.bpm.data.CamundaBpmData.*
+import io.holunda.camunda.bpm.data.CamundaBpmDataKotlin.customVariable
+import io.holunda.camunda.bpm.data.CamundaBpmDataKotlin.dateVariable
+import io.holunda.camunda.bpm.data.CamundaBpmDataKotlin.listVariable
+import io.holunda.camunda.bpm.data.CamundaBpmDataKotlin.mapVariable
+import io.holunda.camunda.bpm.data.CamundaBpmDataKotlin.setVariable
+import io.holunda.camunda.bpm.data.CamundaBpmDataKotlin.stringVariable
import io.holunda.camunda.bpm.data.factory.VariableFactory
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.BOOLEAN
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.BOOLEAN_LOCAL
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX
+import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX_LIST
+import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX_LIST_LOCAL
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX_LOCAL
+import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX_MAP
+import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX_MAP_LOCAL
+import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX_SET
+import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.COMPLEX_SET_LOCAL
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.DATE
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.DATE_LOCAL
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.DOUBLE
@@ -24,8 +36,8 @@ import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Value
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.LIST_STRING_LOCAL
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.LONG
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.LONG_LOCAL
-import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.MAP_STRING_DATE
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.MAP_STRING_DATE_LOCAL
+import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.MAP_STRING_LONG
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.SET_STRING
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.SET_STRING_LOCAL
import io.holunda.camunda.bpm.data.itest.CamundaBpmDataITestBase.Companion.Values.SHORT
@@ -63,7 +75,7 @@ import java.util.*
/**
* Alias for the when
*/
-fun ScenarioTestBase.whenever() = `when`()
+fun ScenarioTestBase.whenever(): W = `when`()
/**
* Base for ITests.
@@ -73,459 +85,523 @@ fun ScenarioTestBase.whenever() = `when`()
@ActiveProfiles("itest")
abstract class CamundaBpmDataITestBase : SpringScenarioTest() {
- companion object {
-
- val STRING_VAR: VariableFactory = stringVariable("String Variable")
- val DATE_VAR: VariableFactory = dateVariable("Date Variable")
- val SHORT_VAR: VariableFactory = shortVariable("Short Variable")
- val INT_VAR: VariableFactory = intVariable("Int Variable")
- val LONG_VAR: VariableFactory = longVariable("Long Variable")
- val DOUBLE_VAR: VariableFactory = doubleVariable("Double Variable")
- val BOOLEAN_VAR: VariableFactory = booleanVariable("Boolean Variable")
- val COMPLEX_VAR: VariableFactory = customVariable("Complex Variable", ComplexDataStructure::class.java)
- val LIST_STRING_VAR: VariableFactory> = listVariable("List Of String Variable", String::class.java)
- val SET_STRING_VAR: VariableFactory> = setVariable("Set Of String Variable", String::class.java)
- val MAP_STRING_DATE_VAR: VariableFactory