From ff5b13e0118412e2300f5a4aef49fb1f517beb5d Mon Sep 17 00:00:00 2001 From: <> Date: Wed, 22 May 2024 20:49:53 +0000 Subject: [PATCH] Deployed 291e838 with MkDocs version: 1.6.0 --- .nojekyll | 0 404.html | 625 ++ advanced-microservice/index.html | 859 +++ advanced-multiple-versions/index.html | 804 ++ advanced-persistence/index.html | 802 ++ advanced-timers/index.html | 889 +++ advanced-zeebe/index.html | 991 +++ assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.081f42fc.min.js | 29 + assets/javascripts/bundle.081f42fc.min.js.map | 7 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.b8dbb3d2.min.js | 42 + .../workers/search.b8dbb3d2.min.js.map | 7 + assets/stylesheets/main.6543a935.min.css | 1 + assets/stylesheets/main.6543a935.min.css.map | 1 + assets/stylesheets/palette.06af60db.min.css | 1 + .../stylesheets/palette.06af60db.min.css.map | 1 + examples/hello_world/hello_world.go | 35 + examples/hello_world/simple_task.bpmn | 41 + examples/hello_world/simple_task.png | Bin 0 -> 5925 bytes .../multiple_versions/multiple_versions.go | 30 + examples/multiple_versions/simple_task.bpmn | 41 + examples/multiple_versions/simple_task.png | Bin 0 -> 12525 bytes .../multiple_versions/simple_task_v2.bpmn | 57 + examples/multiple_versions/simple_task_v2.png | Bin 0 -> 16402 bytes examples/ordering_microservice/index.html | 70 + .../ordering-items-workflow.bpmn | 282 + .../ordering-items-workflow.png | Bin 0 -> 139764 bytes .../ordering_microservice.go | 17 + .../ordering_microservice_bpmn.go | 31 + .../ordering_microservice_order.go | 37 + .../ordering_microservice_order_response.go | 22 + .../ordering_microservice_payments.go | 27 + .../ordering_microservice_routes.go | 12 + .../ordering_microservice_static.go | 30 + .../ordering_microservice_test.go | 7 + .../ordering_microservice/show-process.html | 43 + examples/pause_user_tasks/pause_user_tasks.go | 31 + .../pause_user_tasks/simple-user-task.bpmn | 43 + .../pause_user_tasks/simple-user-task.png | Bin 0 -> 25590 bytes examples/persistence/persistence.go | 35 + examples/persistence/simple-user-task.bpmn | 43 + examples/persistence/simple-user-task.png | Bin 0 -> 25590 bytes examples/timers/timeout-example.bpmn | 146 + examples/timers/timeout-example.png | Bin 0 -> 123659 bytes examples/timers/timers.go | 32 + examples/timers/timers_println.go | 12 + examples/timers/timers_taskhandler.go | 14 + examples/zeebe_exporter/architecture.png | Bin 0 -> 18350 bytes examples/zeebe_exporter/architecture.puml | 20 + examples/zeebe_exporter/simple_task.bpmn | 41 + examples/zeebe_exporter/simple_task.png | Bin 0 -> 5925 bytes .../zeebe-simple-monitor-screenshot.png | Bin 0 -> 507905 bytes examples/zeebe_exporter/zeebe_exporter.go | 39 + expression-syntax/index.html | 1362 ++++ getting-started/index.html | 766 ++ images/end_event.png | Bin 0 -> 4843 bytes images/event_based_gateway.png | Bin 0 -> 8197 bytes images/exclusive_gateway.png | Bin 0 -> 4219 bytes images/gopher-lib-bpmn-engine-128.png | Bin 0 -> 16440 bytes images/gopher-lib-bpmn-engine.svg | 353 + images/inclusive_gateway.png | Bin 0 -> 6771 bytes images/link_intermediate_catch_event.png | Bin 0 -> 8685 bytes images/link_intermediate_throw_event.png | Bin 0 -> 7966 bytes images/message_intermediate_catch_event.png | Bin 0 -> 7572 bytes images/parallel_gateway.png | Bin 0 -> 2191 bytes images/service_task.png | Bin 0 -> 7787 bytes images/start_event.png | Bin 0 -> 4744 bytes images/time_intermediate_catch_event.png | Bin 0 -> 9159 bytes images/user_task.png | Bin 0 -> 7506 bytes implementation-task-handlers/index.html | 896 +++ index.html | 839 +++ roadmap/index.html | 818 ++ search/search_index.json | 1 + sitemap.xml | 58 + sitemap.xml.gz | Bin 0 -> 337 bytes stylesheets/extra.css | 7 + supported-elements/index.html | 1091 +++ 111 files changed, 19689 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 advanced-microservice/index.html create mode 100644 advanced-multiple-versions/index.html create mode 100644 advanced-persistence/index.html create mode 100644 advanced-timers/index.html create mode 100644 advanced-zeebe/index.html create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.081f42fc.min.js create mode 100644 assets/javascripts/bundle.081f42fc.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.b8dbb3d2.min.js create mode 100644 assets/javascripts/workers/search.b8dbb3d2.min.js.map create mode 100644 assets/stylesheets/main.6543a935.min.css create mode 100644 assets/stylesheets/main.6543a935.min.css.map create mode 100644 assets/stylesheets/palette.06af60db.min.css create mode 100644 assets/stylesheets/palette.06af60db.min.css.map create mode 100644 examples/hello_world/hello_world.go create mode 100644 examples/hello_world/simple_task.bpmn create mode 100644 examples/hello_world/simple_task.png create mode 100644 examples/multiple_versions/multiple_versions.go create mode 100644 examples/multiple_versions/simple_task.bpmn create mode 100644 examples/multiple_versions/simple_task.png create mode 100644 examples/multiple_versions/simple_task_v2.bpmn create mode 100644 examples/multiple_versions/simple_task_v2.png create mode 100644 examples/ordering_microservice/index.html create mode 100644 examples/ordering_microservice/ordering-items-workflow.bpmn create mode 100644 examples/ordering_microservice/ordering-items-workflow.png create mode 100644 examples/ordering_microservice/ordering_microservice.go create mode 100644 examples/ordering_microservice/ordering_microservice_bpmn.go create mode 100644 examples/ordering_microservice/ordering_microservice_order.go create mode 100644 examples/ordering_microservice/ordering_microservice_order_response.go create mode 100644 examples/ordering_microservice/ordering_microservice_payments.go create mode 100644 examples/ordering_microservice/ordering_microservice_routes.go create mode 100644 examples/ordering_microservice/ordering_microservice_static.go create mode 100644 examples/ordering_microservice/ordering_microservice_test.go create mode 100644 examples/ordering_microservice/show-process.html create mode 100644 examples/pause_user_tasks/pause_user_tasks.go create mode 100644 examples/pause_user_tasks/simple-user-task.bpmn create mode 100644 examples/pause_user_tasks/simple-user-task.png create mode 100644 examples/persistence/persistence.go create mode 100644 examples/persistence/simple-user-task.bpmn create mode 100644 examples/persistence/simple-user-task.png create mode 100644 examples/timers/timeout-example.bpmn create mode 100644 examples/timers/timeout-example.png create mode 100644 examples/timers/timers.go create mode 100644 examples/timers/timers_println.go create mode 100644 examples/timers/timers_taskhandler.go create mode 100644 examples/zeebe_exporter/architecture.png create mode 100644 examples/zeebe_exporter/architecture.puml create mode 100644 examples/zeebe_exporter/simple_task.bpmn create mode 100644 examples/zeebe_exporter/simple_task.png create mode 100644 examples/zeebe_exporter/zeebe-simple-monitor-screenshot.png create mode 100644 examples/zeebe_exporter/zeebe_exporter.go create mode 100644 expression-syntax/index.html create mode 100644 getting-started/index.html create mode 100644 images/end_event.png create mode 100644 images/event_based_gateway.png create mode 100644 images/exclusive_gateway.png create mode 100644 images/gopher-lib-bpmn-engine-128.png create mode 100644 images/gopher-lib-bpmn-engine.svg create mode 100644 images/inclusive_gateway.png create mode 100644 images/link_intermediate_catch_event.png create mode 100644 images/link_intermediate_throw_event.png create mode 100644 images/message_intermediate_catch_event.png create mode 100644 images/parallel_gateway.png create mode 100644 images/service_task.png create mode 100644 images/start_event.png create mode 100644 images/time_intermediate_catch_event.png create mode 100644 images/user_task.png create mode 100644 implementation-task-handlers/index.html create mode 100644 index.html create mode 100644 roadmap/index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 stylesheets/extra.css create mode 100644 supported-elements/index.html diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..5e8c1064 --- /dev/null +++ b/404.html @@ -0,0 +1,625 @@ + + + +
+ + + + + + + + + + + + + + +The following example snippet shows how a microservice could use BPMN engine +to process orders and provides status feedback to clients.
+For this example, we leverage messages and timers, to orchestrate some tasks. +
+For this microservice, we first define some simple API.
+ + +package main
+
+import "net/http"
+
+func initHttpRoutes() {
+ http.HandleFunc("/api/order", handleOrder) // POST new or GET existing Order
+ http.HandleFunc("/api/receive-payment", handleReceivePayment) // webhook for the payment system
+ http.HandleFunc("/show-process.html", handleShowProcess) // shows the BPMN diagram
+ http.HandleFunc("/index.html", handleIndex) // the index page
+ http.HandleFunc("/", handleIndex) // the index page
+ http.HandleFunc("/ordering-items-workflow.bpmn", handleOrderingItemsWorkflowBpmn) // the BPMN file, for documentation purpose
+}
+
Then we initialize the BPMN engine and register a trivial handler, which just prints on STDOUT.
+ + +package main
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/nitram509/lib-bpmn-engine/pkg/bpmn_engine"
+)
+
+func initBpmnEngine() {
+ bpmnEngine = bpmn_engine.New()
+ process, _ = bpmnEngine.LoadFromBytes(OrderingItemsWorkflowBpmn)
+ bpmnEngine.NewTaskHandler().Id("validate-order").Handler(printHandler)
+ bpmnEngine.NewTaskHandler().Id("send-bill").Handler(printHandler)
+ bpmnEngine.NewTaskHandler().Id("send-friendly-reminder").Handler(printHandler)
+ bpmnEngine.NewTaskHandler().Id("update-accounting").Handler(updateAccountingHandler)
+ bpmnEngine.NewTaskHandler().Id("package-and-deliver").Handler(printHandler)
+ bpmnEngine.NewTaskHandler().Id("send-cancellation").Handler(printHandler)
+}
+
+func printHandler(job bpmn_engine.ActivatedJob) {
+ // do important stuff here
+ println(fmt.Sprintf("%s >>> Executing job '%s'", time.Now(), job.ElementId()))
+ job.Complete()
+}
+
+func updateAccountingHandler(job bpmn_engine.ActivatedJob) {
+ println(fmt.Sprintf("%s >>> Executing job '%s'", time.Now(), job.ElementId()))
+ println(fmt.Sprintf("%s >>> update ledger revenue account with amount=%s", time.Now(), job.Variable("amount")))
+ job.Complete()
+}
+
Since the /api/order
endpoint can be requested with the GET or POST method,
+we need to make the handler smart enough to either create an order process instance or respond a status
package main
+
+import (
+ _ "embed"
+ "fmt"
+ "net/http"
+ "strconv"
+)
+
+func handleOrder(writer http.ResponseWriter, request *http.Request) {
+ if request.Method == "POST" {
+ createNewOrder(writer, request)
+ } else if request.Method == "GET" {
+ showOrderStatus(writer, request)
+ }
+}
+
+func createNewOrder(writer http.ResponseWriter, request *http.Request) {
+ instance, _ := bpmnEngine.CreateAndRunInstance(process.ProcessKey, nil)
+ redirectUrl := fmt.Sprintf("/show-process.html?orderId=%d", instance.GetInstanceKey())
+ http.Redirect(writer, request, redirectUrl, http.StatusFound)
+}
+
+func showOrderStatus(writer http.ResponseWriter, request *http.Request) {
+ orderIdStr := request.URL.Query()["orderId"][0]
+ orderId, _ := strconv.ParseInt(orderIdStr, 10, 64)
+ instance := bpmnEngine.FindProcessInstance(orderId)
+ if instance != nil {
+ // we re-use this GET request to ensure we catch up the timers - ideally the service uses internal timers instead
+ bpmnEngine.RunOrContinueInstance(instance.GetInstanceKey())
+ bytes, _ := prepareJsonResponse(orderIdStr, instance.GetState(), instance.GetCreatedAt())
+ writer.Header().Set("Content-Type", "application/json")
+ writer.Write(bytes)
+ return
+ }
+ http.NotFound(writer, request)
+}
+
Also, for the incoming payments, our microservice provides an endpoint so that we get informed +by external payment service. This handler sends a message to the process instance and continues.
+ + +package main
+
+import (
+ _ "embed"
+ "net/http"
+ "strconv"
+)
+
+func handleReceivePayment(writer http.ResponseWriter, request *http.Request) {
+ orderIdStr := request.FormValue("orderId")
+ amount := request.FormValue("amount")
+ if len(orderIdStr) > 0 && len(amount) > 0 {
+ orderId, _ := strconv.ParseInt(orderIdStr, 10, 64)
+ processInstance := bpmnEngine.FindProcessInstance(orderId)
+ if processInstance != nil {
+ vars := map[string]interface{}{
+ "amount": amount,
+ }
+ bpmnEngine.PublishEventForInstance(processInstance.GetInstanceKey(), "payment-received", vars)
+ bpmnEngine.RunOrContinueInstance(processInstance.GetInstanceKey())
+ http.Redirect(writer, request, "/", http.StatusFound)
+ return
+ }
+ }
+ writer.WriteHeader(400)
+ writer.Write([]byte("Bad request: the request must contain form data with 'orderId' and 'amount', and the order must exist"))
+}
+
To get the snippet compile, see the other sources in the +examples/ordering_microservice/ folder.
+ + + + + + + + + + + + + +The lib-bpmn-engine supports working with multiple version of a BPMN process. +Typically, you want to do this, when you have long-running processes instances in-flight, +and you want to e.g. bug-fix or improve a BPMN process. +In such a scenario, you can't alter in-flight instances, but in parallel loading a newer v2 version, +and create new instances from this v2 process. +You could then decide to cancel older v1 process instances or simply wait for them to complete.
+Consider you have two hello world processes ...
+v1 \ +
+v2 \ +
+Both definitions have the same ID=hello-world-process-id
.
+The engine will load both and assign version=2
to the second process.
+Creating new instances is then possible by either knowing the process key (which is returned when loading the BPMN),
+or by knowing the ID. The example below uses the latter.
Hint: the handler will be called twice, since in v2, both service task have the same type.
+ + +package main
+
+import (
+ "github.com/nitram509/lib-bpmn-engine/pkg/bpmn_engine"
+)
+
+func main() {
+ // create a new named engine
+ bpmnEngine := bpmn_engine.New()
+ // basic example loading a v1 BPMN from file,
+ _, err := bpmnEngine.LoadFromFile("simple_task.bpmn")
+ if err != nil {
+ panic("file \"simple_task.bpmn\" can't be read. " + err.Error())
+ }
+ // now loading v2, basically with the same process ID
+ _, err = bpmnEngine.LoadFromFile("simple_task_v2.bpmn")
+ if err != nil {
+ panic("file \"simple_task.bpmn\" can't be read. " + err.Error())
+ }
+
+ // register a handler for a service task by defined task type
+ bpmnEngine.NewTaskHandler().Type("hello-world").Handler(printElementIdHandler)
+ // and execute the process, means we will use v2
+ bpmnEngine.CreateAndRunInstanceById("hello-world-process-id", nil)
+}
+
+func printElementIdHandler(job bpmn_engine.ActivatedJob) {
+ println(job.ElementId())
+ job.Complete() // don't forget this one, or job.Fail("foobar")
+}
+
To get the snippet compile, see the full sources in the +examples/timers/ folder.
+ + + + + + + + + + + + + +The lib-bpmn-engine supports persistence (a.k.a. marshalling or serialization), +which can be used to pause workflows, store them on disk and resume later. +The data format is plain JSON, which you as the user of the lib must store and load. +By design, the lib will not support any specific database technology.
+When calling bpmnEngine.Marshal()
, the whole engine including all process instances is exported.
+When you have a large amount of process instances, it's recommended to rather use multiple
+engine instances, one per process instance, to keep the exported data small and efficient.
For this example, we're just using a simple human task, which is supposed to be stored on disk.
+ + + +package main
+
+import "github.com/nitram509/lib-bpmn-engine/pkg/bpmn_engine"
+
+func main() {
+ bpmnEngine := bpmn_engine.New()
+ process, _ := bpmnEngine.LoadFromFile("simple-user-task.bpmn")
+ bpmnEngine.NewTaskHandler().Assignee("assignee").Handler(doNothingHandler())
+ instance, _ := bpmnEngine.CreateAndRunInstance(process.ProcessKey, nil)
+ instanceKey := instance.InstanceKey // remember the key for later continuation
+
+ // export the whole engine state as bytes
+ // the export format is valid JSON and can be stored however you want
+ bytes := bpmnEngine.Marshal()
+
+ // debug print ... in real-live the data is stored to disk/database
+ println(string(bytes))
+
+ resumeWorkflow(bytes, instanceKey)
+}
+
+func resumeWorkflow(bytes []byte, processInstanceKey int64) {
+ // import the bytes
+ newBpmnEngine, _ := bpmn_engine.Unmarshal(bytes)
+
+ // and resume the workflow for the give process instance key
+ _, _ = newBpmnEngine.RunOrContinueInstance(processInstanceKey)
+}
+
+func doNothingHandler() func(job bpmn_engine.ActivatedJob) {
+ return func(job bpmn_engine.ActivatedJob) {
+ println("Do nothing, which keeps the job active.")
+ // HINT: to complete a job, the handler must call `job.Complete()`
+ }
+}
+
To get the snippet compile, see the full sources in the +./examples/persistence/ folder.
+ + + + + + + + + + + + + +The lib-bpmn-engine supports timer intermediate catch events, +which are very useful to model typical timeout scenarios.
+ +The one above "ask $1 million question" demonstrates a 10 seconds timeout +to give the correct answer or lose the whole "game". +This is a best-practice example, for how to model (business) timeouts or deadlines.
+In BPMN processes, Timer Intermediate Catch events can be used in combination with +Event Based Gateway, to exclusively select one execution path in the process. +When a timer event happens before a message event, then the example $1 million question game is lost.
+The Problem: implementing a timer/scheduler very much depends on your context or non-functional requirements. +E.g. you might run lib-bpmn-engine as part of a single batch job instance OR you have a web service +implement which is running with 3 instances. Both scenarios do require different implementation approaches, +how to deal with long-running processes.
+Choices: Depending on your scenario/use case, you might implement a trivial blocking loop, +like in the example code below. +In multi-instance environments, you might better use a central scheduler, to avoid each instance of the +application (using lib-bpmn-engine) is doing its own un-coordinated timing/scheduling.
+In a nutshell: +The lib-bpmn-engine does create such timer event objects and will pause the process execution. +This means, an external ticker/scheduler is required, to continue the process instance.
+The code snippet below demonstrates a trivial example, how to execute processes with timers. +Here, the execution is blocking until the due time is reached. +This might fit in a scenario, where you have a single instance running in a batch-job like environment.
+Depending on your context, you might choose some external ticker/scheduler, +to check for active scheduled timer events.
+ + +package main
+
+import (
+ "github.com/nitram509/lib-bpmn-engine/pkg/bpmn_engine"
+ "time"
+)
+
+func main() {
+ bpmnEngine := bpmn_engine.New()
+ process, err := bpmnEngine.LoadFromFile("timeout-example.bpmn")
+ if err != nil {
+ panic("file \"timeout-example.bpmn\" can't be read.")
+ }
+ // just some dummy handler to complete the tasks/jobs
+ registerDummyTaskHandlers(&bpmnEngine)
+
+ instance, err := bpmnEngine.CreateAndRunInstance(process.ProcessKey, nil)
+ println(instance.GetState()) // still ACTIVE at this point
+
+ printScheduledTimerInformation(bpmnEngine.GetTimersScheduled()[0])
+
+ // sleep() for 2 seconds, before trying to continue the process instance
+ // this for-loop essentially will block until the process instance has completed OR an error occurred
+ for ; instance.GetState() == bpmn_engine.Active && err == nil; time.Sleep(2 * time.Second) {
+ println("tick.")
+ // by re-running, the engine will check for active timers and might continue execution,
+ // if timer.DueAt has passed
+ _, err = bpmnEngine.RunOrContinueInstance(instance.GetInstanceKey())
+ }
+
+ println(instance.GetState()) // finally completed
+}
+
To get the snippet compile, see the full sources in the +examples/timers/ folder.
+ + + + + + + + + + + + + +Since lib-bpmn-engine runs embedded in your application, +it's a challenge, to "see what's happen inside". Therefore, this library exports +all internal events and you can register various event exporters. +That might be a simple STDOUT exporter, or one that exports to a dedicated visualisation application.
+There's an Open Source Web UI for monitoring BPMN processes Zeebe Simple Monitor. +Fortunately, the authors did design the monitor as an event consumer. +Thus, by exporting Zeebe compatible events, you're able to monitor your processes.
+As shown in the architecture diagram, your App and Zeebe Simple Montor don't +connect to each other, but rather your app needs to connect to a Hazelcast Ringbuffer. +Once connected, the built-in Zeebe Exporter will export events to this Ringbuffer. +The Zeebe Simple Monitor (ZSM) is a Java based application, which connects to the Ringbuffer +as well and fetches events from there. Once fetched, ZSM stores events in it's own database. +The ZSM database can be configured to your preferences.
+Please, keep in mind, this architecture does not support any namespacing. +Means, when multiple of your applications connect to the same Ringbuffer, +all events will be mixed up. That's less an issue technically, but might confuse your users.
+ +As with the first and experimental release, just a handful of events are supported. +This means, in contract to a full flavoured Zeebe cluster, events are missing +and will not be shown. That said, basic functionality in Simple Monitor is given.
+Once you application restarts, workflows are newly deployed and so they are shown +multiple times in Zeebe Simple Monitor (ZSM). This is because of the design/architecture +of lib-bpmn-engine. With a new start of your app, a new ID will be assigned +and ZSM ha no chance to detect former workflows/processes are identical.
+Housekeeping is missing in ZSM, which results in events just stack up and will slow down your database.
+The ringbuffer support in Hazelcast's Go client is not yet officially merged. +So, lib-bpmn-engine uses a feature branch of the client to write to the ringbuffer.
+Here are some hints, how you can quickly spin up a setup for experimenting. +If you aim for a more production ready setup, please read&learn how to do such +with Hazelcast and Zeebe Simple Monitor on their official web sites individually.
+First, you need a running Hazelcast. The simplest way is using Docker... +
+Using Docker, also allows you to start a Zeebe Simple Monitor... +
docker run -p 8082:8082 -e "zeebe.client.worker.hazelcast.connection=$(hostname):5701" ghcr.io/camunda-community-hub/zeebe-simple-monitor:2.4.0```
+
⚠️ The above Docker containers do expose each service without any authentication! +This mean, anyone in your network can connect to it - please, use a proper firewall +or other tools to secure your system.
+package main
+
+import (
+ "context"
+ "fmt"
+ "github.com/hazelcast/hazelcast-go-client"
+ "github.com/nitram509/lib-bpmn-engine/pkg/bpmn_engine"
+ "github.com/nitram509/lib-bpmn-engine/pkg/bpmn_engine/exporter/zeebe"
+)
+
+func main() {
+ // create a new named engine
+ bpmnEngine := bpmn_engine.New()
+ // the exporter will require a running Hazelcast cluster at 127.0.0.1:5701
+ ctx := context.TODO()
+ config := hazelcast.Config{}
+ config.Cluster.Network.SetAddresses("localhost:5701")
+ client, err := hazelcast.StartNewClientWithConfig(ctx, config)
+ // create the client
+ exporter, _ := zeebe.NewExporterWithHazelcastClient(client)
+ // register the exporter
+ bpmnEngine.AddEventExporter(&exporter)
+ // basic example loading a BPMN from file,
+ process, err := bpmnEngine.LoadFromFile("simple_task.bpmn")
+ if err != nil {
+ panic("file \"simple_task.bpmn\" can't be read.")
+ }
+ // register a handler for a service task by defined task type
+ bpmnEngine.NewTaskHandler().Id("hello-world").Handler(printContextHandler)
+ // and execute the process
+ instance, _ := bpmnEngine.CreateAndRunInstance(process.ProcessKey, nil)
+
+ println(fmt.Sprintf("instanceKey=%d", instance.GetInstanceKey()))
+}
+
+func printContextHandler(job bpmn_engine.ActivatedJob) {
+ // trivial handler is requires
+ job.Complete()
+}
+