Repository navigation
Connecting a workflow module
A workflow module reaches the cockpit through one dependency and a handful of configuration keys. This page is the setup. What your business code then writes is on Reporting workflows and user tasks.
-
What has to be there already
The three things the cockpit adapter builds on, and why it needs each of them. -
The one dependency
Which artifact to add, per BPMS and per platform. -
The smallest configuration which works
Where the cockpit server is, and what it has to know about your module. -
REST, Kafka or a way of your own
The ways a report travels, and how to choose one. -
How a module registers itself
What happens at startup, and what a cockpit which is down changes about it. -
What the cockpit asks back
The endpoints your module answers once it is registered.
Your application runs on VanillaBP Version 2, with the BPMS adapter for the engine your workflows run on. The cockpit adapter listens to that adapter's engine; it does not run anything itself.
Your application has an outbox store. Every report is written into it first and sent afterwards, in the same transaction as the workflow aggregate the report is about. That is what makes a rolled back workflow report nothing and a cockpit server which is down lose nothing. An application whose aggregates live in more than one store is fine: each report goes into the store of the case it belongs to.
Your workflow module has an id, which is VanillaBP's own, and it is the id the cockpit knows it by. An application with several workflow modules configures each of them.
One artifact per BPMS and platform. It brings the neutral half of the cockpit adapter with it, and
that half brings io.vanillabp.businesscockpit:spi-for-java, so the annotations your business code
uses are on the classpath without a second dependency.
| BPMS | Spring Boot | Quarkus |
|---|---|---|
| Camunda 7 | businesscockpit-camunda7-adapter-spring-boot |
businesscockpit-camunda7-adapter-quarkus |
| Camunda 8 | businesscockpit-camunda8-adapter-spring-boot |
businesscockpit-camunda8-adapter-quarkus |
| Process-Engine-API | businesscockpit-process-engine-api-adapter-spring-boot |
businesscockpit-process-engine-api-adapter-quarkus |
All of them are published under the group io.vanillabp.businesscockpit. Which version to pick, and
what the half for your engine does with your models, is in that adapter's own wiki:
Camunda 7,
Camunda 8,
Process-Engine-API.
The Camunda 8 adapter is published once per Camunda 8 minor, so its version carries the line it was
built for.
<dependency>
<groupId>io.vanillabp.businesscockpit</groupId>
<artifactId>businesscockpit-camunda7-adapter-spring-boot</artifactId>
<version>0.9.0-SNAPSHOT</version>
</dependency>Two things: where the cockpit server is, and what the cockpit has to know about your workflow module.
vanillabp:
cockpit:
rest:
base-url: http://localhost:8080
workflow-modules:
taxi-ride:
cockpit:
workflow-module-uri: http://localhost:8081/taxi-ride
ui-uri-type: WEBPACK_MF_REACT
ui-uri-path: /remoteEntry.js
i18n-languages:
- en
- de
bpmn-description-language: en
initiator-source: by-applicationworkflow-module-uri is the address the cockpit reaches your module at, both for the questions it
asks and for the forms it loads. ui-uri-type and ui-uri-path say how the forms are served;
User task forms and status sites is about those two.
i18n-languages names the languages your titles are reported in, and bpmn-description-language
says which language the names in your BPMN files are written in.
initiator-source has no default, and a reporting module without it does not start. Write
by-application where your details providers set the initiator themselves, which is the usual
case, and system where this module knows no action a user causes. Nobody can work out later who
caused a case, so a case which ran without an initiator stays without one.
Reporting workflows and user tasks says where the value comes
from and what happens when it is missing at runtime.
A module whose user interface the cockpit cannot load writes the whole address of one of its own
pages as ui-uri-path instead, starting with http:// or https://. Such an address is opened as
it is, and opening a task takes the user there in a window of its own. ui-uri-type then carries
the name your cockpit user interface agreed on for such modules. Your details provider may hand out an address per task, so that the user lands on
the very ticket the task is about.
A gap here ends the boot with a message naming the key which fills it, and every gap of the application is reported in one boot rather than one at a time. A workflow module which configures none of these keys reports nothing at all, and the log says so while the application starts. Every key, and the four levels a value may be written at, is on Configuration.
A report travels the way you choose. Two ways come with the cockpit adapter, and you configure one of them. Neither of them, or both, ends the boot with a message naming the keys of both. The third way is one you write yourself, and it needs no key at all.
REST is the straightforward one. vanillabp.cockpit.rest.base-url is the address of the cockpit
server's BPMS API, and choosing that key chooses this way. The report is a POST, and the answer says
whether the cockpit took it. A cockpit which is down means a failed call, the outbox entry stays,
and it is repeated with a backoff until the server takes it. The connection is yours to describe:
basic authentication or the client credentials flow, timeouts, a proxy, a truststore.
Kafka is the other one. vanillabp.cockpit.kafka.bootstrap-servers names the brokers and chooses
this way, and three topics carry the three kinds of report. The adapter builds its own producer, so
anything else the producer needs goes into vanillabp.cockpit.kafka.properties.* and is handed to
the Kafka client unchanged. Kafka takes the cockpit out of the sending path: the broker accepts the
report and the cockpit reads it when it is up again.
Which one to take is mostly an operations question. REST is one less thing to run, and it tells you straight away whether the cockpit understood the report. Kafka is the obvious choice where your other services already talk over it.
Your own way is a bean of type BusinessCockpitTransport. Where the application has one, the
cockpit adapter reports through it and leaves both shipped transports alone. The interface has one
method per kind of report, and each of them is called while an outbox entry is dispatched. Your
transport is therefore handed a finished report, and it inherits what the dispatch does around it. A
call which fails is repeated with a backoff. The call runs in the transaction of the workflow
aggregate. Several reports about one user task have already collapsed into the one you are given.
On Spring Boot the bean is an ordinary one, and the shipped transport steps aside for it the way version 1's publishing beans did:
@Bean
public BusinessCockpitTransport ourOwnTransport() {
return new OurOwnTransport();
}On Quarkus it is a @Singleton class or a @Produces method. The adapter declares its own transport
as ArC's @DefaultBean, so yours wins without any qualifier.
Such an application configures neither vanillabp.cockpit.rest.base-url nor
vanillabp.cockpit.kafka.bootstrap-servers, and it starts all the same. The boot says that the
reports go through the bean you brought. Where one of the two keys is set as well, the adapter does
not read it and the boot says so, because a key nobody reads is worse than no key. Both keys next to
your own bean end the boot, since nothing then says which of the two transports you meant.
To send what a shipped transport sends and do something more with it, wrap it. Ask for the bean of
type BusinessCockpitConfiguration and build the shipped transport from it:
@Bean
public BusinessCockpitTransport ourOwnTransport(
final BusinessCockpitConfiguration configuration) {
return new OurOwnTransport(BusinessCockpitAssembly.transportOf(configuration));
}BusinessCockpitTransport is a published interface of the cockpit adapter. It is not changed
without an upgrade note, so a transport of your own is as safe to keep as the configuration keys
are.
A workflow module tells the cockpit about itself once the application is up. The registration is an outbox entry like every other report, so a cockpit server which is down while your application starts does not stop it from booting. The entry waits and is repeated.
The registration carries the module id, the address the module answers at, and how its user interface is served. From that the cockpit opens a path of its own for the module and forwards everything below it, which is how the browser loads your forms without ever talking to your application directly.
It happens after the workflow modules are deployed rather than during the deployment. On Quarkus the outbox store creates its table in a startup step of its own, and an entry written any earlier would find no table. Both platforms do it at the same point, so the two behave alike.
A registered module is asked questions, and it answers them on workflow-module-uri. The endpoints
are generated from an OpenAPI specification, so a module implements the parts it cares about and
leaves the rest.
Two of them matter to most applications. One says which groups may see this workflow module at all,
which is what the WorkflowModuleDetailsProvider bean answers, described on
Reporting workflows and user tasks. The other serves the user
interface of the module, which the cockpit's proxy forwards to.
Everything else the cockpit knows about your tasks and cases it knows because you reported it. That is the next page.
