{"id":1879,"date":"2018-08-30T00:00:00","date_gmt":"2018-08-29T22:00:00","guid":{"rendered":"https:\/\/wwwneu.strehle.de\/tim\/weblog\/archives\/2018\/08\/30\/1638-2\/"},"modified":"2025-07-31T21:37:07","modified_gmt":"2025-07-31T19:37:07","slug":"1638-2","status":"publish","type":"post","link":"https:\/\/www.strehle.de\/tim\/weblog\/archives\/2018\/08\/30\/1638-2\/","title":{"rendered":"Playing with the Camunda workflow engine (and PHP)"},"content":{"rendered":"\n<p>A generic workflow engine, configured via a graphical diagram editor on top of an XML syntax \u2013 that\u2019s what I tried and failed to develop more than 15 years ago. I did help build three generations of <a href=\"https:\/\/www.digicol.de\/dc-x-importers-and-the-workflow-engine\/\">a simple \u201cworkflow\u201d component<\/a> integrated in our DAM product to drive asset ingestion and export, kept reading (see <a href=\"\/tim\/weblog\/archives\/2004\/05\/27\/351\">The State of Workflow<\/a> and <a href=\"\/tim\/weblog\/archives\/2005\/11\/22\/583\">Decoupling Application Logic<\/a>) and writing (<a href=\"\/tim\/weblog\/archives\/2015\/04\/29\/1760\">Workflow awareness of DAM systems<\/a>) about workflows \u2013 and hoping that one day, a powerful and beautiful workflow management system would make my work easier.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Camunda<\/h3>\n\n\n\n<p>That\u2019s why I was thrilled to discover <a href=\"https:\/\/camunda.com\">Camunda<\/a>, a workflow engine with an open source, free community edition. It is standards-based, written in Java, and comes with a Web UI, REST API and graphical process diagram modeler. Here\u2019s a screenshot of <a href=\"https:\/\/camunda.com\/products\/modeler\/\">Camunda Modeler<\/a>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/s3.eu-central-1.amazonaws.com\/files.strehle.de\/tim\/blog\/2018-09-01-camunda-modeler-with-external-task.jpg\"><img decoding=\"async\" src=\"https:\/\/s3.eu-central-1.amazonaws.com\/files.strehle.de\/tim\/blog\/2018-09-01-camunda-modeler-with-external-task.jpg\" alt=\"Camunda Modeler screenshot\"\/><\/a><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Camunda typically \u201cdrives\u201d a workflow process by actively executing your Java code when there\u2019s a task to perform. But you can also use <a href=\"https:\/\/docs.camunda.org\/manual\/latest\/user-guide\/process-engine\/external-tasks\/\">external tasks<\/a> (see \u201cImplementation: External\u201d and \u201cTopic\u201d in the screenshot above) to turn a task into a queue to be handled by external workers via the <a href=\"https:\/\/docs.camunda.org\/manual\/latest\/reference\/rest\/\">Camunda REST API<\/a>. That pattern is useful for PHP developers like me, but also especially suited to <a href=\"https:\/\/camunda.com\/solutions\/microservices-orchestration\/\">service orchestration<\/a>. The workflow engine sits there passively, taking only care of workflow definitions, instances, their flow and state. (More on that in the <a href=\"https:\/\/vimeo.com\/176585915\">External Tasks for Service Orchestration<\/a> webinar video.)<\/p>\n\n\n\n<p>While Camunda is trivial to <a href=\"https:\/\/docs.camunda.org\/manual\/latest\/installation\/full\/\">install<\/a> and extensively documented, it took me a while to grasp the basic concepts \u2013 process definitions, process instances, tasks, gateways, variables \u2013 and I\u2019m only scratching the surface so far.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Using Camunda from PHP<\/h3>\n\n\n\n<p>My example workflow is a minimal \u201casset processing pipeline\u201d, extracting metadata from an asset file (using ExifTool) and rendering a thumbnail preview image (using ImageMagick) for images. This isn\u2019t \u201cservice orchestration\u201d, but a good enough test case to help me get to know Camunda.<\/p>\n\n\n\n<p>First, I drew the above diagram in Camunda Modeler and deployed it to the server. Now I could start a new process instance using curl:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ curl -X POST \\\n  http:\/\/localhost:8080\/engine-rest\/process-definition\/key\/asset-ingestion\/start \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"variables\":{\"path\":{\"value\":\"\/tmp\/img.jpg\"}}}'\n<\/code><\/pre>\n\n\n\n<p>I used the <a href=\"https:\/\/github.com\/endpot\/camunda-rest-client\">camunda-rest-client<\/a> PHP library (not an official library) to implement a simple worker process that fetches and locks an open task, calls ExifTool or ImageMagick and marks the task as completed. Asset and thumbnail filenames are stored in process instance variables.<\/p>\n\n\n\n<p>You can find all of <a href=\"https:\/\/gist.github.com\/tistre\/70aea9dce8cf4e32d9761cd4b6271309\">my PHP code on Github<\/a>, along with the process definition XML. Here\u2019s some interesting code snippets:<\/p>\n\n\n\n<p><strong>Fetching the next open task<\/strong>, and locking it for exclusive processing by the current worker process:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$externalTaskService = new ExternalTaskService($camundaUrl);\n\n$externalTaskQueryRequest = (new ExternalTaskRequest())\n    -&gt;set('topics', &#91;&#91;'topicName' =&gt; $topicName, 'lockDuration' =&gt; $lockDuration]])\n    -&gt;set('workerId', $workerId)\n    -&gt;set('maxTasks', 1);\n\n\/\/ Returns an array of task objects\n$externalTasks = $externalTaskService-&gt;fetchAndLock($externalTaskQueryRequest);\n<\/code><\/pre>\n\n\n\n<p><strong>Reading process instance variables:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$assetFilePath = $externalTask-&gt;variables-&gt;path-&gt;value;\n<\/code><\/pre>\n\n\n\n<p><strong>Telling Camunda that the task is completed<\/strong> when the worker is done (so Camunda can automatically move on to the next workflow task), adding task results to the process instance using variables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$externalTaskRequest = (new ExternalTaskRequest())\n    -&gt;set('variables', $updateVariables)\n    -&gt;set('workerId', $workerId);\n\n$externalTaskService-&gt;complete($externalTask-&gt;id, $externalTaskRequest);\n<\/code><\/pre>\n\n\n\n<p><strong>Notifying Camunda that the task failed<\/strong> (which makes Camunda report an \u201cincident\u201d):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$externalTaskRequest = (new ExternalTaskRequest())\n    -&gt;set('errorMessage', $errorMessage)\n    -&gt;set('retries', $retries)\n    -&gt;set('retryTimeout', $retryTimeout)\n    -&gt;set('workerId', $workerId);\n\n$this-&gt;externalTaskService-&gt;handleFailure($externalTask-&gt;id, $externalTaskRequest);\n<\/code><\/pre>\n\n\n\n<p>In my example, I need to instantiate at least one worker process per task \u201ctopic\u201d. Multiple instances would automatically process open tasks in parallel. <strong>Running a worker<\/strong> looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ php worker.php \\\n  --camunda-url=\"http:\/\/localhost:8080\/engine-rest\" \\\n  --task-topic=\"asset-extract-metadata\"\nFetched and locked &lt;asset-extract-metadata&gt; task &lt;00a74f0a-abcd-11e8-afcc-02422b22e161&gt; \n  of &lt;asset-ingestion&gt; process instance &lt;00a664a4-abcd-11e8-afcc-02422b22e161&gt;.\nCompleted task &lt;00a74f0a-abcd-11e8-afcc-02422b22e161&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Batteries included<\/h3>\n\n\n\n<p>I love it when software developers care for transparency and visualization. <a href=\"https:\/\/docs.camunda.org\/manual\/latest\/webapps\/cockpit\/\">Camunda Cockpit<\/a> lets you peek into currently-running processes by showing how many of them are in which state, and letting you inspect and even change their variables:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/s3.eu-central-1.amazonaws.com\/files.strehle.de\/tim\/blog\/2018-09-01-camunda-cockpit-process-instance.jpg\"><img decoding=\"async\" src=\"https:\/\/s3.eu-central-1.amazonaws.com\/files.strehle.de\/tim\/blog\/2018-09-01-camunda-cockpit-process-instance.jpg\" alt=\"Camunda Cockpit screenshot\"\/><\/a><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>It\u2019s pretty amazing that a simple diagram plus a single 300-line PHP file get me a separation of process flow and task implementation, queues, parallel worker processes, error handling, and insights into executions.<\/p>\n\n\n\n<p>I will definitely keep playing with Camunda. If you\u2019re already using it in production, I\u2019m happy to hear from you!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A generic workflow engine, configured via a graphical diagram editor on top of an XML syntax \u2013 that\u2019s what I tried and failed to develop more than 15 years ago. I did help build three generations of a simple \u201cworkflow\u201d component integrated in our DAM product to drive asset ingestion and export, kept reading (see [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_share_on_mastodon":"0"},"categories":[1],"tags":[],"class_list":["post-1879","post","type-post","status-publish","format-standard","hentry","category-weblog"],"share_on_mastodon":{"url":"","error":""},"_links":{"self":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts\/1879","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/comments?post=1879"}],"version-history":[{"count":1,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts\/1879\/revisions"}],"predecessor-version":[{"id":1893,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts\/1879\/revisions\/1893"}],"wp:attachment":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/media?parent=1879"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/categories?post=1879"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/tags?post=1879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}