This is an automated email from the ASF dual-hosted git repository.

fjtiradosarti pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-examples.git


The following commit(s) were added to refs/heads/main by this push:
     new b79fdfdaa [SRVLOGIC-777] OpenTelemetry E2E automated IT in examples 
(#2164)
b79fdfdaa is described below

commit b79fdfdaa123951634184c82fabb460b0ed258bd
Author: Gonzalo Muñoz <[email protected]>
AuthorDate: Fri Feb 6 11:55:33 2026 +0100

    [SRVLOGIC-777] OpenTelemetry E2E automated IT in examples (#2164)
---
 serverless-workflow-examples/pom.xml               |   3 +-
 .../README.md                                      | 177 ++++++++
 .../pom.xml                                        | 264 +++++++++++
 .../sw/opentelemetry/jaeger/ErrorFunctions.java    |  30 ++
 .../sw/opentelemetry/jaeger/RegisterResource.java  |  41 ++
 .../src/main/resources/application.properties      |  76 ++++
 .../src/main/resources/error.sw.json               |  34 ++
 .../src/main/resources/greet.sw.json               |  71 +++
 .../src/main/resources/greetSubflow.sw.json        |  42 ++
 .../src/main/resources/persist-wait.sw.json        |  75 ++++
 .../src/main/resources/specs/persist-callback.yaml |  21 +
 .../sw/opentelemetry/jaeger/JaegerQueryClient.java |  98 +++++
 .../opentelemetry/jaeger/JaegerTestResource.java   | 125 ++++++
 .../jaeger/OpenTelemetryJaegerIT.java              |  30 ++
 .../jaeger/OpenTelemetryJaegerTest.java            | 149 +++++++
 .../opentelemetry/jaeger/helper/JaegerHelper.java  | 485 +++++++++++++++++++++
 .../opentelemetry/jaeger/helper/JaegerPoller.java  |  79 ++++
 .../jaeger/helper/JaegerQueryClient.java           | 177 ++++++++
 .../jaeger/persistence/CloudEvents.java            |  45 ++
 .../jaeger/persistence/PersistWait01StartIT.java   |  70 +++
 .../jaeger/persistence/PersistWait02ResumeIT.java  | 131 ++++++
 .../jaeger/persistence/PersistWaitPidStore.java    |  53 +++
 .../jaeger/persistence/PostgresTestResource.java   |  54 +++
 23 files changed, 2329 insertions(+), 1 deletion(-)

diff --git a/serverless-workflow-examples/pom.xml 
b/serverless-workflow-examples/pom.xml
index a83c8bf9d..4e3ce6776 100644
--- a/serverless-workflow-examples/pom.xml
+++ b/serverless-workflow-examples/pom.xml
@@ -81,7 +81,8 @@
         
<module>serverless-workflow-timeouts-showcase-operator-devprofile</module>
         <module>serverless-workflow-python-quarkus</module>
         <module>sonataflow-fluent</module>
-        <module>serverless-workflow-fault-tolerance</module>
+       <module>serverless-workflow-fault-tolerance</module>
+       <module>serverless-workflow-opentelemetry-jaeger-quarkus</module>
       </modules>
     </profile>
 
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/README.md
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/README.md
new file mode 100644
index 000000000..5ffb62d45
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/README.md
@@ -0,0 +1,177 @@
+# Serverless Workflow – OpenTelemetry (Quarkus) + Jaeger
+This module demonstrates **end-to-end OpenTelemetry tracing** for
+**SonataFlow** running on **Quarkus**, with
+traces exported to **Jaeger** and validated via **integration tests**.
+
+It covers:
+- HTTP entrypoints
+- Workflow execution spans
+- Subflows
+- Error paths
+- Persistence + resume via events
+- Native image execution (GraalVM)
+
+All traces are asserted by querying Jaeger directly.
+
+---
+
+## What is being tested
+
+The module contains **6 integration tests**, grouped by concern.
+
+### 1. `OpenTelemetryJaegerIT`
+**Baseline tracing validation**
+
+Verifies that:
+- HTTP requests generate server spans
+- Workflow execution produces `sonataflow.process.*` spans
+- Mandatory workflow tags are present:
+  - `sonataflow.process.id`
+  - `sonataflow.process.instance.id`
+  - `sonataflow.workflow.state`
+- Traces are correctly exported to Jaeger via OTLP
+
+This is the “smoke test” for OpenTelemetry wiring.
+
+---
+
+### 2. `OpenTelemetryJaegerIT` (transaction fallback)
+**Transaction propagation logic**
+
+Validates that:
+- When an explicit transaction header is present, it is propagated
+- When the transaction header is missing, the workflow **falls back to the 
process instance id**
+- The resolved transaction id is visible in workflow spans
+
+This ensures correlation is always available in traces.
+
+---
+
+### 3. `OpenTelemetryJaegerIT` (subflow)
+**Subflow tracing and correlation**
+
+Validates that:
+- A parent workflow calling a subflow generates **multiple workflow spans**
+- Parent and subflow spans share the same:
+  - `sonataflow.transaction.id`
+  - `sonataflow.process.instance.id`
+- Subflow execution is visible in Jaeger as part of the same trace
+
+This confirms **subflow-workflow trace continuity**.
+
+---
+
+### 4. `PersistWait01StartIT`
+**Persistent workflow start**
+
+Validates that:
+- A workflow configured with persistence starts correctly
+- The process instance id is generated and stored
+- A trace exists for the “start + persist” phase
+- Workflow state before suspension is visible in tracing data
+
+This test proves persistence does not break tracing.
+
+---
+
+### 5. `PersistWait02ResumeIT`
+**Resume after persistence via event**
+
+Validates that:
+- A persisted workflow is resumed via a CloudEvent (`/resume`)
+- The workflow continues execution from the suspended state
+- Context (transaction id, instance id, workflow state) is preserved
+- Traces after resume belong to the same logical workflow execution
+
+This is the key test proving **persistence + events + tracing** work together.
+
+---
+
+### 6. `OpenTelemetryJaegerIT` (error workflow)
+**Error propagation in traces**
+
+Validates that:
+- A failing workflow produces error spans
+- Error conditions are visible in Jaeger
+- Workflow metadata is still attached to errored spans
+
+This ensures observability even when workflows fail.
+
+All tests are executed **both in JVM mode and native mode**.
+
+---
+
+## Debugging Jaeger traces
+
+You can enable verbose trace logging during test execution by passing:
+
+```sh
+-Dtest.jaeger.debug=true
+```
+
+When this flag is enabled, tests will print:
+
+- All span operation names found in the trace
+
+- Workflow-related tags (sonataflow.*, transaction, tracker)
+
+- Workflow state transitions observed in the trace
+
+The flag is disabled by default to keep CI logs concise.
+
+---
+
+## Native image testing
+
+Note that this requires GRAALVM_HOME to point to a valid GraalVM installation
+
+```sh
+mvn clean install -Pnative
+```
+
+Run the full integration test suite against the native binary
+
+- GraalVM builds a native image and wire extensions at build time
+- Persistence, Flyway, OpenTelemetry, and messaging must all work correctly
+- Tracing must survive native compilation
+
+### How native tests are executed
+- The application is built as a native binary
+- The binary is launched by Quarkus test infrastructure
+- Testcontainers provide:
+  - Jaeger (OTLP + Query)
+  - PostgreSQL (workflow persistence)
+- The same tests assert behavior against the native binary
+
+
+---
+
+## Supporting infrastructure
+
+During tests, the following containers are started automatically:
+
+- **Jaeger all-in-one**
+  - OTLP gRPC 
+  - Query API used for assertions
+- **PostgreSQL**
+  - Used by SonataFlow persistence
+  - Schema initialized via Flyway
+
+No external services are required.
+
+---
+
+## Summary
+
+This module provides a **reference example** for assurance of:
+
+- OpenTelemetry tracing for SonataFlow workflows
+- Jaeger-based trace validation
+- Workflow correlation and context propagation
+- Persistent workflows with event-based resume
+- JVM and native execution parity
+
+It is intended as both:
+- A regression quality test
+- A blueprint for production-ready observability setups
+
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/pom.xml
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/pom.xml
new file mode 100644
index 000000000..41e3a3ddd
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/pom.xml
@@ -0,0 +1,264 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.kie.kogito.examples</groupId>
+    <artifactId>serverless-workflow-examples-parent</artifactId>
+    <version>999-SNAPSHOT</version>
+    <relativePath>../serverless-workflow-examples-parent/pom.xml</relativePath>
+  </parent>
+
+  <groupId>org.kie.kogito.examples</groupId>
+  <artifactId>serverless-workflow-opentelemetry-jaeger-quarkus</artifactId>
+  <version>1.0-SNAPSHOT</version>
+
+  <packaging>jar</packaging>
+  <name>Kogito Example :: Serverless Workflow :: OpenTelemetry</name>
+  <description>Kogito Serverless Workflow OpenTelemetry - Quarkus</description>
+
+  <properties>
+    <quarkus-plugin.version>3.20.3</quarkus-plugin.version>
+    <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+    <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
+    <quarkus.platform.version>3.20.3</quarkus.platform.version>
+    <kogito.bom.group-id>org.kie.kogito</kogito.bom.group-id>
+    <kogito.bom.artifact-id>kogito-bom</kogito.bom.artifact-id>
+    <kogito.bom.version>999-SNAPSHOT</kogito.bom.version>
+    <version.org.kie.kogito>999-SNAPSHOT</version.org.kie.kogito>
+    <maven.compiler.release>17</maven.compiler.release>
+    <version.compiler.plugin>3.13.0</version.compiler.plugin>
+    <version.surefire.plugin>3.3.1</version.surefire.plugin>
+    
<version.failsafe.plugin>${version.surefire.plugin}</version.failsafe.plugin>
+    <testcontainers.version>1.19.7</testcontainers.version>
+    <okhttp.version>4.12.0</okhttp.version>
+  </properties>
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>${quarkus.platform.group-id}</groupId>
+        <artifactId>${quarkus.platform.artifact-id}</artifactId>
+        <version>${quarkus.platform.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>${kogito.bom.group-id}</groupId>
+        <artifactId>${kogito.bom.artifact-id}</artifactId>
+        <version>${kogito.bom.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>org.testcontainers</groupId>
+        <artifactId>testcontainers-bom</artifactId>
+        <version>${testcontainers.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.kie.sonataflow</groupId>
+      <artifactId>sonataflow-quarkus</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.quarkus</groupId>
+      <artifactId>quarkus-resteasy</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.quarkus</groupId>
+      <artifactId>quarkus-resteasy-jackson</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.quarkus</groupId>
+      <artifactId>quarkus-smallrye-health</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.quarkus</groupId>
+      <artifactId>quarkus-opentelemetry</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.kie.sonataflow</groupId>
+      <artifactId>sonataflow-addons-quarkus-opentelemetry</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.kie</groupId>
+      <artifactId>kie-addons-quarkus-persistence-jdbc</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.quarkus</groupId>
+      <artifactId>quarkus-jdbc-postgresql</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.squareup.okhttp3</groupId>
+      <artifactId>okhttp</artifactId>
+      <version>${okhttp.version}</version>
+    </dependency>
+
+    <!-- Tests -->
+    <dependency>
+      <groupId>io.quarkus</groupId>
+      <artifactId>quarkus-junit5</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.rest-assured</groupId>
+      <artifactId>rest-assured</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.awaitility</groupId>
+      <artifactId>awaitility</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.testcontainers</groupId>
+      <artifactId>testcontainers</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.testcontainers</groupId>
+      <artifactId>junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.testcontainers</groupId>
+      <artifactId>postgresql</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>${version.compiler.plugin}</version>
+        <configuration>
+          <release>${maven.compiler.release}</release>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>${quarkus.platform.group-id}</groupId>
+        <artifactId>quarkus-maven-plugin</artifactId>
+        <version>${quarkus-plugin.version}</version>
+        <extensions>true</extensions>
+        <executions>
+          <execution>
+            <goals>
+              <goal>build</goal>
+              <goal>generate-code</goal>
+              <goal>generate-code-tests</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <version>${version.failsafe.plugin}</version>
+        <configuration>
+          <systemPropertyVariables>
+            
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+            <maven.home>${maven.home}</maven.home>
+          </systemPropertyVariables>
+          <parallel>none</parallel>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>com.github.ekryd.sortpom</groupId>
+        <artifactId>sortpom-maven-plugin</artifactId>
+        <version>4.0.0</version>
+        <configuration>
+          <lineSeparator>\n</lineSeparator>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>sort</goal>
+            </goals>
+            <phase>validate</phase>
+          </execution>
+        </executions>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <profiles>
+    <!-- Common pattern in examples to build container images -->
+    <profile>
+      <id>container</id>
+      <activation>
+        <property>
+          <name>container</name>
+        </property>
+      </activation>
+      <properties>
+        <quarkus.profile>container</quarkus.profile>
+      </properties>
+      <dependencies>
+        <dependency>
+          <groupId>io.quarkus</groupId>
+          <artifactId>quarkus-container-image-jib</artifactId>
+        </dependency>
+      </dependencies>
+    </profile>
+    <profile>
+      <id>native</id>
+      <activation>
+        <property>
+          <name>native</name>
+        </property>
+      </activation>
+      <properties>
+        <quarkus.native.enabled>true</quarkus.native.enabled>
+      </properties>
+    </profile>
+  </profiles>
+
+</project>
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/ErrorFunctions.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/ErrorFunctions.java
new file mode 100644
index 000000000..cd984061b
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/ErrorFunctions.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class ErrorFunctions {
+
+    public void fail(String message) {
+        throw new IllegalStateException(message);
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/RegisterResource.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/RegisterResource.java
new file mode 100644
index 000000000..c18d2ef6c
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/RegisterResource.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger;
+
+import java.util.Map;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+@Path("/register")
+public class RegisterResource {
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Map<String, Object> register(JsonNode payload) {
+        return Map.of("registered", true);
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/application.properties
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/application.properties
new file mode 100644
index 000000000..a7bc4a412
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/application.properties
@@ -0,0 +1,76 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+quarkus.devservices.enabled=false
+quarkus.keycloak.devservices.enabled=false
+
+# Run KIE persistence migrations via Quarkus Flyway (works in native)
+quarkus.flyway.enabled=true
+quarkus.flyway.migrate-at-start=true
+quarkus.flyway.clean-at-start=false
+#quarkus.flyway.locations=classpath:kie-flyway/db/persistence-jdbc/postgresql
+
+# Prevent double-migration
+kie.flyway.enabled=false
+
+# Ensure native image contains the migration SQLs
+quarkus.native.resources.includes=kie-flyway/db/persistence-jdbc/.*
+
+quarkus.native.native-image-xmx=8g
+
+quarkus.application.name=sw-opentelemetry-jaeger-example
+
+quarkus.otel.enabled=true
+
+# Give Jaeger a stable service name to query
+quarkus.otel.service.name=sw-opentelemetry-jaeger-example
+
+# profile to pack this example into a container, to use it execute activate 
the maven container profile, -Dcontainer
+%container.quarkus.container-image.build=true
+%container.quarkus.container-image.push=false
+%container.quarkus.container-image.group=${USER}
+%container.quarkus.container-image.registry=dev.local
+%container.quarkus.container-image.tag=1.0-SNAPSHOT
+
+# Point Flyway at the KIE migrations
+%prod.quarkus.flyway.locations=classpath:kie-flyway/db/persistence-jdbc/postgresql
+%prod.quarkus.flyway.enabled=true
+%prod.quarkus.flyway.migrate-at-start=true
+%prod.quarkus.flyway.clean-at-start=false
+%prod.quarkus.flyway.baseline-on-migrate=false
+
+%prod.kie.flyway.enabled=false
+
+%integration-test.quarkus.datasource.db-kind=postgresql
+%integration-test.quarkus.datasource.jdbc.url=${quarkus.datasource.jdbc.url}
+%integration-test.quarkus.datasource.username=${quarkus.datasource.username}
+%integration-test.quarkus.datasource.password=${quarkus.datasource.password}
+
+%integration-test.callbackBaseUrl=${test.url}
+
+quarkus.log.level=INFO
+quarkus.log.category."io.quarkus".level=INFO
+quarkus.log.category."org.testcontainers".level=INFO
+
+quarkus.rest-client."persist_callback_yaml".url=${test.url}
+
+mp.messaging.incoming.resume.connector=quarkus-http
+mp.messaging.incoming.resume.path=/resume
+mp.messaging.incoming.resume.method=POST
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/error.sw.json
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/error.sw.json
new file mode 100644
index 000000000..38c606fc6
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/error.sw.json
@@ -0,0 +1,34 @@
+{
+  "id": "error",
+  "version": "1.0",
+  "specVersion": "0.8",
+  "name": "Error workflow",
+  "description": "Deterministic failure to validate error traces in Jaeger.",
+  "start": "FailNow",
+  "states": [
+    {
+      "name": "FailNow",
+      "type": "operation",
+      "actions": [
+        {
+          "name": "fail",
+          "functionRef": {
+            "refName": "failFn",
+            "arguments": {
+              "message": "Intentional failure for opentelemetry tests"
+            }
+          }
+        }
+      ],
+      "end": true
+    }
+  ],
+  "functions": [
+    {
+      "name": "failFn",
+      "type": "custom",
+      "operation": 
"service:org.kie.kogito.examples.sw.opentelemetry.jaeger.ErrorFunctions::fail"
+    }
+  ]
+}
+
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/greet.sw.json
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/greet.sw.json
new file mode 100644
index 000000000..0c79708ad
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/greet.sw.json
@@ -0,0 +1,71 @@
+{
+  "id": "greet",
+  "version": "1.0",
+  "specVersion": "0.8",
+  "name": "Greet parent workflow",
+  "description": "Parent workflow that logs, calls a subflow, then returns a 
static response.",
+  "start": "Init",
+  "states": [
+    {
+      "name": "Init",
+      "type": "operation",
+      "actions": [
+        {
+          "name": "log-start",
+          "functionRef": {
+            "refName": "logInfo",
+            "arguments": {
+              "message": "Parent workflow started"
+            }
+          }
+        }
+      ],
+      "transition": "CallSubflow"
+    },
+    {
+       "name": "CallSubflow",
+       "type": "operation",
+       "actions": [
+       {
+         "name": "invoke-subflow",
+         "subFlowRef": {
+            "workflowId": "greetSubflow"
+         }
+       }
+       ],
+       "transition": "Finish"
+    },
+    {
+      "name": "Finish",
+      "type": "operation",
+      "actions": [
+        {
+          "name": "log-end",
+          "functionRef": {
+            "refName": "logInfo",
+            "arguments": {
+              "message": "Parent workflow completed"
+            }
+          }
+        }
+      ],
+      "transition": "Done"
+    },
+    {
+      "name": "Done",
+      "type": "inject",
+      "data": {
+        "message": "Hello from parent workflow"
+      },
+      "end": true
+    }
+  ],
+  "functions": [
+    {
+      "name": "logInfo",
+      "type": "custom",
+      "operation": "sysout:INFO"
+    }
+  ]
+}
+
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/greetSubflow.sw.json
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/greetSubflow.sw.json
new file mode 100644
index 000000000..7e5840f46
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/greetSubflow.sw.json
@@ -0,0 +1,42 @@
+{
+  "id": "greetSubflow",
+  "version": "1.0",
+  "specVersion": "0.8",
+  "name": "Greet subflow",
+  "description": "Subflow that logs and returns a static response.",
+  "start": "SubflowLog",
+  "states": [
+    {
+      "name": "SubflowLog",
+      "type": "operation",
+      "actions": [
+        {
+          "name": "log-subflow",
+          "functionRef": {
+            "refName": "logInfo",
+            "arguments": {
+              "message": "Subflow executed"
+            }
+          }
+        }
+      ],
+      "transition": "SubflowDone"
+    },
+    {
+      "name": "SubflowDone",
+      "type": "inject",
+      "data": {
+        "message": "Hello from subflow"
+      },
+      "end": true
+    }
+  ],
+  "functions": [
+    {
+      "name": "logInfo",
+      "type": "custom",
+      "operation": "sysout:INFO"
+    }
+  ]
+}
+
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/persist-wait.sw.json
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/persist-wait.sw.json
new file mode 100644
index 000000000..8daa2db76
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/persist-wait.sw.json
@@ -0,0 +1,75 @@
+{
+  "id": "persistWait",
+  "version": "1.0",
+  "name": "Persistence + Callback resume example",
+  "description": "Waits for an HTTP callback event and resumes after restart",
+  "start": "printWaitMessage",
+  "events": [
+    {
+      "name": "resumeEvent",
+      "source": "",
+      "type": "resume"
+    }
+  ],
+  "functions": [
+    {
+      "name": "callBack",
+      "type": "rest",
+      "operation": "specs/persist-callback.yaml#callBack"
+    },
+    {
+      "name": "printMessage",
+      "type": "custom",
+      "operation": "sysout:INFO"
+    }
+  ],
+  "states": [
+    {
+      "name": "printWaitMessage",
+      "type": "operation",
+      "actions": [
+        {
+          "name": "printBeforeEvent",
+          "functionRef": {
+            "refName": "printMessage",
+            "arguments": {
+              "message": "Init - waiting for resume event"
+            }
+          }
+        }
+      ],
+      "transition": "waitForEvent"
+    },
+    {
+      "name": "waitForEvent",
+      "type": "callback",
+      "action": {
+        "functionRef": {
+          "refName": "callBack",
+          "arguments": {
+            "processInstanceId": "$WORKFLOW.instanceId"
+          }
+        }
+      },
+      "eventRef": "resumeEvent",
+      "transition": "finish"
+    },
+    {
+      "name": "finish",
+      "type": "operation",
+      "actions": [
+        {
+          "name": "printAfterEvent",
+          "functionRef": {
+            "refName": "printMessage",
+            "arguments": {
+              "message": "After resume - context should be restored"
+            }
+          }
+        }
+      ],
+      "end": true
+    }
+  ]
+}
+
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/specs/persist-callback.yaml
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/specs/persist-callback.yaml
new file mode 100644
index 000000000..1d3469486
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/main/resources/specs/persist-callback.yaml
@@ -0,0 +1,21 @@
+openapi: 3.0.3
+info:
+  title: Persist Callback
+  version: "1.0"
+paths:
+  /register:
+    post:
+      operationId: callBack
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                processInstanceId:
+                  type: string
+      responses:
+        "202":
+          description: Accepted
+
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/JaegerQueryClient.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/JaegerQueryClient.java
new file mode 100644
index 000000000..c67a99e61
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/JaegerQueryClient.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Minimal Jaeger Query API client for tests. Uses endpoints: - GET 
/api/services - GET
+ * /api/traces?service=...&limit=... - GET /api/traces/{traceId}
+ */
+public class JaegerQueryClient {
+
+    private final HttpClient http;
+    private final ObjectMapper mapper;
+    private final String baseUrl;
+
+    public JaegerQueryClient(String baseUrl) {
+        this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, 
baseUrl.length() - 1) : baseUrl;
+        this.http = 
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
+        this.mapper = new ObjectMapper();
+    }
+
+    public List<String> listServices() {
+        JsonNode root = getJson("/api/services");
+        List<String> services = new ArrayList<>();
+        JsonNode data = root.get("data");
+        if (data != null && data.isArray()) {
+            data.forEach(n -> services.add(n.asText()));
+        }
+        return services;
+    }
+
+    public Optional<String> findLatestTraceIdForService(String serviceName, 
int limit) {
+        String qs = "?service=" + urlEncode(serviceName) + "&limit=" + limit;
+        JsonNode root = getJson("/api/traces" + qs);
+        JsonNode data = root.get("data");
+        if (data == null || !data.isArray() || data.isEmpty()) {
+            return Optional.empty();
+        }
+        // The API generally returns recent traces first, but we keep it 
simple:
+        JsonNode first = data.get(0);
+        JsonNode traceId = first.get("traceID");
+        return traceId == null ? Optional.empty() : 
Optional.of(traceId.asText());
+    }
+
+    public JsonNode getTrace(String traceId) {
+        return getJson("/api/traces/" + urlEncode(traceId));
+    }
+
+    private JsonNode getJson(String path) {
+        try {
+            HttpRequest req = HttpRequest.newBuilder().uri(URI.create(baseUrl 
+ path)).timeout(Duration.ofSeconds(10))
+                    .GET().build();
+            HttpResponse<String> resp = http.send(req, 
HttpResponse.BodyHandlers.ofString());
+            if (resp.statusCode() != 200) {
+                throw new IllegalStateException(
+                        "Jaeger API call failed: " + path + " status=" + 
resp.statusCode() + " body=" + resp.body());
+            }
+            return mapper.readTree(resp.body());
+        } catch (IOException | InterruptedException e) {
+            throw new RuntimeException("Jaeger API call failed: " + path, e);
+        }
+    }
+
+    private static String urlEncode(String s) {
+        return URLEncoder.encode(s, StandardCharsets.UTF_8);
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/JaegerTestResource.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/JaegerTestResource.java
new file mode 100644
index 000000000..a283bd71a
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/JaegerTestResource.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.logging.Logger;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+
+/**
+ * Starts a Jaeger all-in-one container (with OTLP ingestion enabled) for 
tests. Exposes: - Jaeger Query/UI: 16686 -
+ * OTLP gRPC: 4317 - OTLP HTTP: 4318 Provides Quarkus config so the app 
exports traces to Jaeger over OTLP.
+ */
+public class JaegerTestResource implements QuarkusTestResourceLifecycleManager 
{
+
+    private static final Logger LOGGER = 
Logger.getLogger(JaegerTestResource.class);
+
+    // Pin a specific version for CI stability (avoid "latest")
+    private static final String JAEGER_IMAGE = "jaegertracing/all-in-one:1.54";
+
+    private static final int JAEGER_QUERY_PORT = 16686;
+    private static final int OTLP_GRPC_PORT = 4317;
+    private static final int OTLP_HTTP_PORT = 4318;
+
+    private GenericContainer<?> jaeger;
+
+    @Override
+    public Map<String, String> start() {
+        jaeger = new GenericContainer<>(JAEGER_IMAGE)
+                .withExposedPorts(JAEGER_QUERY_PORT, OTLP_GRPC_PORT, 
OTLP_HTTP_PORT)
+                // Enable OTLP ingestion in Jaeger all-in-one
+                .withEnv("COLLECTOR_OTLP_ENABLED", "true")
+                // Wait until Jaeger query API is responsive (better than only 
port-open)
+                
.waitingFor(Wait.forHttp("/api/services").forPort(JAEGER_QUERY_PORT).forStatusCode(200)
+                        .withStartupTimeout(Duration.ofSeconds(60)));
+
+        jaeger.start();
+
+        String host = jaeger.getHost();
+        Integer mappedOtlpGrpc = jaeger.getMappedPort(OTLP_GRPC_PORT);
+        Integer mappedOtlpHttp = jaeger.getMappedPort(OTLP_HTTP_PORT);
+        Integer mappedQuery = jaeger.getMappedPort(JAEGER_QUERY_PORT);
+
+        // OTLP endpoint for Quarkus OTel exporter.
+        // For gRPC, Quarkus typically expects http://host:port and 
protocol=grpc.
+        String otlpGrpcEndpoint = "http://"; + host + ":" + mappedOtlpGrpc;
+
+        // Jaeger Query URL (handy for tests that call the Jaeger HTTP API)
+        String jaegerQueryBaseUrl = "http://"; + host + ":" + mappedQuery;
+
+        LOGGER.infof("Jaeger started: query=%s, otlpGrpc=%s, 
otlpHttp=http://%s:%d";, jaegerQueryBaseUrl,
+                otlpGrpcEndpoint, host, mappedOtlpHttp);
+
+        Map<String, String> cfg = new HashMap<>();
+
+        // --- OTel export configuration (set both common variants to be safe 
across Quarkus lines) ---
+        // cfg.put("quarkus.otel.traces.exporter", "otlp");
+
+        // Preferred OTLP exporter config (Quarkus 3.x commonly uses these)
+        cfg.put("quarkus.otel.exporter.otlp.endpoint", otlpGrpcEndpoint);
+        cfg.put("quarkus.otel.exporter.otlp.protocol", "grpc");
+
+        // Some setups split traces endpoint (harmless if ignored)
+        cfg.put("quarkus.otel.exporter.otlp.traces.endpoint", 
otlpGrpcEndpoint);
+        cfg.put("quarkus.otel.exporter.otlp.traces.protocol", "grpc");
+
+        // Optional: make Jaeger query base URL available to the tests
+        cfg.put("test.jaeger.query.base-url", jaegerQueryBaseUrl);
+
+        return cfg;
+    }
+
+    @Override
+    public void stop() {
+        if (jaeger != null) {
+            try {
+                jaeger.stop();
+            } catch (Exception e) {
+                LOGGER.warn("Failed to stop Jaeger container cleanly", e);
+            } finally {
+                jaeger = null;
+            }
+        }
+    }
+
+    /**
+     * Convenience accessors for tests (optional). Note: These require the 
resource instance to be started in the same
+     * JVM.
+     */
+    public String getJaegerQueryBaseUrl() {
+        if (jaeger == null) {
+            throw new IllegalStateException("Jaeger container is not started");
+        }
+        return "http://"; + jaeger.getHost() + ":" + 
jaeger.getMappedPort(JAEGER_QUERY_PORT);
+    }
+
+    public String getOtlpGrpcEndpoint() {
+        if (jaeger == null) {
+            throw new IllegalStateException("Jaeger container is not started");
+        }
+        return "http://"; + jaeger.getHost() + ":" + 
jaeger.getMappedPort(OTLP_GRPC_PORT);
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/OpenTelemetryJaegerIT.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/OpenTelemetryJaegerIT.java
new file mode 100644
index 000000000..c551b3e2b
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/OpenTelemetryJaegerIT.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+/**
+ * Runs OpenTelemetryJaegerTest as a Quarkus integration test (packaged 
artifact). This is the class that Failsafe will
+ * pick up.
+ */
+@QuarkusIntegrationTest
+public class OpenTelemetryJaegerIT extends OpenTelemetryJaegerTest {
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/OpenTelemetryJaegerTest.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/OpenTelemetryJaegerTest.java
new file mode 100644
index 000000000..b9684d91a
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/OpenTelemetryJaegerTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.is;
+
+import java.time.Duration;
+import java.util.Optional;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import org.awaitility.Awaitility;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.kie.kogito.examples.sw.opentelemetry.jaeger.helper.JaegerPoller;
+import 
org.kie.kogito.examples.sw.opentelemetry.jaeger.helper.JaegerQueryClient;
+import static 
org.kie.kogito.examples.sw.opentelemetry.jaeger.helper.JaegerHelper.*;
+
+@QuarkusTestResource(JaegerTestResource.class)
+public abstract class OpenTelemetryJaegerTest {
+
+    private static final String GREET_ENDPOINT = "/greet";
+    private static final String ERROR_ENDPOINT = "/error";
+
+    /**
+     * Test 1: traces exist and contain SonataFlow tags + transaction/tracker 
tags
+     */
+    @Test
+    void test1_tracesExistAndContainSonataflowTags() {
+        triggerWorkflow(GREET_ENDPOINT, "{\"name\":\"Ada\"}", "txn-001", 
"corr-123");
+
+        JsonNode trace = 
waitForTraceWithTracker("sonataflow.tracker.correlation_id", "corr-123");
+
+        assertHasAnySpans(trace);
+
+        String processInstanceId = assertAnyWorkflowSpanHasTag(trace, 
"sonataflow.process.instance.id");
+        assertFalse(processInstanceId.isBlank());
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.process.id", "greet");
+        assertAnyWorkflowSpanHasTag(trace, 
"sonataflow.tracker.correlation_id", "corr-123");
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.transaction.id", 
"txn-001");
+
+        assertHasAtLeastOneHttpServerSpan(trace);
+        assertWorkflowStatesContainAtLeast(trace, "Init", "CallSubflow", 
"Finish", "Done");
+    }
+
+    /**
+     * Test 2: fallback when no X-TRANSACTION-ID header: transaction tag sets 
to process.instance.id
+     */
+    @Test
+    void test2_transactionFallbackWhenHeaderMissing() {
+        // No transaction header
+        given().header("X-TRACKER-correlation_id", "corr-fallback-1")
+              .contentType("application/json")
+               .body("{\"name\":\"Edu\"}")
+       .when()
+              .post(GREET_ENDPOINT)
+       .then()
+              .statusCode(anyOf(is(200), is(201)));
+
+        JsonNode trace = 
waitForTraceWithTracker("sonataflow.tracker.correlation_id", "corr-fallback-1");
+
+        String processInstanceId = assertAnyWorkflowSpanHasTag(trace, 
"sonataflow.process.instance.id");
+        assertFalse(processInstanceId.isBlank());
+
+        // transactiond.id is sent with processInstanceId
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.transaction.id", 
processInstanceId);
+    }
+
+    /**
+     * Test 3: subflow correlation Parent workflow triggers subflow. 
+     * Validate tracker + transaction are present
+     * (correlation across parent/subflow)
+     */
+    @Test
+    void test3_subflowPlaceholderStateIsTracedAndCorrelated() {
+        given().header("X-TRANSACTION-ID", "txn-subflow-001")
+              .header("X-TRACKER-testcase", "t3-subflow")
+               .contentType("application/json")
+              .body("{\"name\":\"Ada\"}")
+       .when()
+              .post(GREET_ENDPOINT).then()
+               .statusCode(anyOf(is(200), is(201)));
+
+        JsonNode trace = 
waitForTraceWithTracker("sonataflow.tracker.testcase", "t3-subflow");
+
+        assertWorkflowStatesContainAtLeast(trace, "Init", "CallSubflow", 
"Done");
+
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.transaction.id", 
"txn-subflow-001");
+
+        String pid = assertAnyWorkflowSpanHasTag(trace, 
"sonataflow.process.instance.id");
+        assertFalse(pid.isBlank());
+
+        JsonNode callSubflowSpan = assertWorkflowHasAnyState(trace, 
"CallSubflow");
+        assertSpanHasTag(callSubflowSpan, "sonataflow.transaction.id");
+
+        JsonNode parentSpan = assertAnySpanOperationNameEquals(trace, 
"sonataflow.process.greet.execute");
+        JsonNode subflowSpan = assertAnySpanOperationNameEquals(trace, 
"sonataflow.process.greetSubflow.execute");
+
+        String parentTx = requireTagValue(parentSpan, 
"sonataflow.transaction.id");
+        String subflowTx = requireTagValue(subflowSpan, 
"sonataflow.transaction.id");
+
+        assertEquals(parentTx, subflowTx, "Transaction id must match between 
parent and subflow spans");
+    }
+
+    /**
+     * Test 4: error workflow shows error
+     */
+    @Test
+    void test4_errorWorkflowShowsError() {
+        given().header("X-TRANSACTION-ID", "txn-error-001")
+              .header("X-TRACKER-testcase", "t4-error")
+               .contentType("application/json")
+              .body("{\"name\":\"Soul\"}")
+       .when()
+              .post(ERROR_ENDPOINT).then()
+               .statusCode(anyOf(is(400), is(500)));
+
+        JsonNode trace = 
waitForTraceWithTracker("sonataflow.tracker.testcase", "t4-error");
+
+        assertAnySpanLooksErrored(trace);
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.transaction.id", 
"txn-error-001");
+    }
+
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerHelper.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerHelper.java
new file mode 100644
index 000000000..54d83a522
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerHelper.java
@@ -0,0 +1,485 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.helper;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.Set;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.is;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+
+import org.jboss.logging.Logger;
+
+import org.awaitility.Awaitility;
+
+/**
+ * Helper methods for Jaeger trace JSON. 
+ * Jaeger trace format: GET /api/traces/{traceId} 
+ * Response: { "data": [{"traceID": "...", "spans": [...] } ] }
+ */
+public final class JaegerHelper {
+
+    private static final Logger LOGGER = Logger.getLogger(JaegerHelper.class);
+    private static final String DEBUG_JAEGER_PROP = "test.jaeger.debug";
+
+    // Must match application.properties: quarkus.otel.service.name
+    public static final String SERVICE_NAME = 
"sw-opentelemetry-jaeger-example";
+    public static final String JAEGER_QUERY_BASE_URL_KEY = 
"test.jaeger.query.base-url";
+
+    public static final Duration TIMEOUT = Duration.ofSeconds(30);
+    public static final Duration POLL = Duration.ofMillis(500);
+
+    private JaegerHelper() {
+    }
+
+    public static JsonNode extractFirstTrace(JsonNode traceResponse) {
+        JsonNode data = traceResponse.get("data");
+        if (data == null || !data.isArray() || data.isEmpty()) {
+            throw new AssertionError("Expected Jaeger response to contain 
non-empty 'data' array");
+        }
+        return data.get(0);
+    }
+
+    public static void assertHasAnySpans(JsonNode jaegerTrace) {
+        JsonNode spans = jaegerTrace.get("spans");
+        if (spans == null || !spans.isArray() || spans.isEmpty()) {
+            throw new AssertionError("Expected trace to contain non-empty 
'spans' array");
+        }
+    }
+
+    public static String assertAnyWorkflowSpanHasTag(JsonNode trace, String 
key) {
+        return assertAnyWorkflowSpanHasTag(trace, key, null, false);
+    }
+
+    public static String assertAnyWorkflowSpanHasTag(JsonNode trace, String 
key, String expectedValue) {
+        return assertAnyWorkflowSpanHasTag(trace, key, expectedValue, true);
+    }
+
+    public static void assertHasAtLeastOneHttpServerSpan(JsonNode trace) {
+        JsonNode spans = trace.get("spans");
+        if (spans == null || !spans.isArray() || spans.isEmpty()) {
+            throw new AssertionError("No spans found in trace");
+        }
+
+        for (JsonNode span : spans) {
+            String op = span.hasNonNull("operationName") ? 
span.get("operationName").asText() : "";
+            if (op.startsWith("POST ") || op.startsWith("GET ") || 
op.startsWith("PUT ") || op.startsWith("DELETE ")) {
+                return;
+            }
+            // Alternative: check for span.kind=server tag
+            if (spanHasTag(span, "span.kind", "server")) {
+                return;
+            }
+        }
+
+        throw new AssertionError(
+                "Did not find any HTTP server span (operationName starting 
with HTTP verb or span.kind=server)");
+    }
+
+    public static void assertWorkflowStatesContainAtLeast(JsonNode trace, 
String... expectedStates) {
+        Set<String> states = collectWorkflowStates(trace);
+        for (String s : expectedStates) {
+            if (!states.contains(s)) {
+                throw new AssertionError("Expected workflow states to contain 
'" + s + "', but states were: " + states);
+            }
+        }
+    }
+
+    public static JsonNode assertWorkflowHasAnyState(JsonNode trace, String 
state) {
+        return findWorkflowSpanByState(trace, state)
+                .orElseThrow(() -> new AssertionError("Did not find workflow 
span with state '" + state + "'"));
+    }
+
+    public static String assertSpanHasTag(JsonNode span, String tagKey) {
+        return findTagValue(span, tagKey)
+                .orElseThrow(() -> new AssertionError("Expected span to 
contain tag '" + tagKey + "'"));
+    }
+
+    public static Optional<String> findTagValue(JsonNode span, String tagKey) {
+        JsonNode tags = span.get("tags");
+        if (tags == null || !tags.isArray()) {
+            return Optional.empty();
+        }
+
+        for (JsonNode tag : tags) {
+            if (tagKey.equals(tag.path("key").asText())) {
+                return Optional.ofNullable(tag.path("value").asText(null));
+            }
+        }
+        return Optional.empty();
+    }
+
+    public static Set<String> collectWorkflowStates(JsonNode trace) {
+        Set<String> states = new HashSet<>();
+        JsonNode spans = trace.get("spans");
+        if (spans == null || !spans.isArray()) {
+            return states;
+        }
+
+        for (JsonNode span : spans) {
+            String op = span.hasNonNull("operationName") ? 
span.get("operationName").asText() : "";
+            if (!op.startsWith("sonataflow.process.")) {
+                continue;
+            }
+
+            JsonNode tags = span.get("tags");
+            if (tags == null || !tags.isArray()) {
+                continue;
+            }
+
+            for (JsonNode tag : tags) {
+                String key = tag.hasNonNull("key") ? tag.get("key").asText() : 
"";
+                if ("sonataflow.workflow.state".equals(key)) {
+                    String val = tag.hasNonNull("value") ? 
tag.get("value").asText() : null;
+                    if (val != null && !val.isBlank()) {
+                        states.add(val);
+                    }
+                }
+            }
+        }
+        return states;
+    }
+
+    public static Optional<JsonNode> findWorkflowSpanByState(JsonNode trace, 
String expectedState) {
+        JsonNode spans = trace.get("spans");
+        if (spans == null || !spans.isArray()) {
+            return Optional.empty();
+        }
+
+        for (JsonNode span : spans) {
+            if (!isWorkflowSpan(span)) {
+                continue;
+            }
+            Optional<String> state = findTagValue(span, 
"sonataflow.workflow.state");
+            if (state.isPresent() && expectedState.equals(state.get())) {
+                return Optional.of(span);
+            }
+        }
+        return Optional.empty();
+    }
+
+    public static boolean isWorkflowSpan(JsonNode span) {
+        if (span == null) {
+            return false;
+        }
+
+        JsonNode op = span.get("operationName");
+        if (op != null && op.isTextual() && 
op.asText().startsWith("sonataflow.process.")) {
+            return true;
+        }
+
+        JsonNode tags = span.get("tags");
+        if (tags == null || !tags.isArray()) {
+            return false;
+        }
+
+        for (JsonNode tag : tags) {
+            JsonNode keyNode = tag.get("key");
+            if (keyNode != null && keyNode.isTextual()) {
+                String key = keyNode.asText();
+                if (key.startsWith("sonataflow.")) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public static JsonNode assertAnySpanOperationNameEquals(JsonNode trace, 
String expectedOperationName) {
+        JsonNode spans = trace.get("spans");
+        if (spans == null || !spans.isArray()) {
+            throw new AssertionError("Trace JSON missing spans array");
+        }
+
+        for (JsonNode span : spans) {
+            String op = span.path("operationName").asText("");
+            if (expectedOperationName.equals(op)) {
+                return span;
+            }
+        }
+
+        throw new AssertionError("Did not find any span with operationName='" 
+ expectedOperationName + "'");
+    }
+
+    public static String requireTagValue(JsonNode span, String key) {
+        JsonNode tags = span.get("tags");
+        if (tags == null || !tags.isArray()) {
+            throw new AssertionError("Span JSON missing tags array");
+        }
+
+        for (JsonNode tag : tags) {
+            if (key.equals(tag.path("key").asText())) {
+                String value = tag.path("value").asText(null);
+                if (value == null || value.isBlank()) {
+                    throw new AssertionError("Tag '" + key + "' value is 
empty");
+                }
+                return value;
+            }
+        }
+        throw new AssertionError("Did not find tag '" + key + "' on span");
+    }
+
+    public static void assertAnySpanLooksErrored(JsonNode jaegerTrace) {
+        JsonNode spans = jaegerTrace.get("spans");
+        if (spans == null || !spans.isArray()) {
+            throw new AssertionError("Trace JSON missing spans array");
+        }
+        for (JsonNode span : spans) {
+            JsonNode tags = span.get("tags");
+            if (tags == null || !tags.isArray()) {
+                continue;
+            }
+            for (JsonNode tag : tags) {
+                String key = tag.hasNonNull("key") ? tag.get("key").asText() : 
"";
+                if ("error".equalsIgnoreCase(key)) {
+                    JsonNode v = tag.get("value");
+                    if (v != null && (v.asBoolean(false) || 
"true".equalsIgnoreCase(v.asText()))) {
+                        return;
+                    }
+                }
+                if (key.toLowerCase().contains("exception") || 
key.toLowerCase().contains("status")) {
+                    return;
+                }
+            }
+        }
+        throw new AssertionError("Did not find any span with an error 
indicator tag (e.g., error=true)");
+    }
+
+    public static void triggerWorkflow(String endpoint, String body, String 
transactionId, String correlationId) {
+        given().header("X-TRANSACTION-ID", transactionId)
+              .header("X-TRACKER-correlation_id", correlationId)
+               .contentType("application/json")
+              .body(body)
+       .when()
+              .post(endpoint).then()
+               .statusCode(anyOf(is(200), is(201)));
+    }
+
+    public static JsonNode waitForTraceWithTracker(String trackerKey, String 
trackerValue) {
+        return waitForTraceWithTracker(trackerKey, trackerValue, null, new 
String[0]);
+    }
+
+    public static JsonNode waitForTraceWithTracker(String trackerKey, String 
trackerValue,
+            String expectedProcessInstanceId, String... requiredStates) {
+        JaegerQueryClient jaeger = new JaegerQueryClient(getJaegerBaseUrl());
+
+        
Awaitility.await().atMost(Duration.ofSeconds(30)).pollInterval(Duration.ofMillis(500))
+                .until(() -> jaeger.listServices().contains(SERVICE_NAME));
+
+        return 
Awaitility.await().atMost(Duration.ofSeconds(30)).pollInterval(Duration.ofMillis(500)).until(()
 -> {
+            // Look at multiple recent traces, not only "latest"
+            List<String> traceIds = 
jaeger.findTraceIdsForService(SERVICE_NAME, 30);
+            if (traceIds.isEmpty()) {
+                return null;
+            }
+
+            for (String traceId : traceIds) {
+                JsonNode resp = jaeger.getTrace(traceId); // {data:[{...}]}
+                JsonNode data = resp.get("data");
+                if (data == null || !data.isArray() || data.isEmpty()) {
+                    continue;
+                }
+                JsonNode trace = data.get(0);
+
+                // Must have tracker on a workflow span
+                if (!containsWorkflowSpanTag(trace, trackerKey, trackerValue)) 
{
+                    continue;
+                }
+
+                // If PID is provided, require it too
+                if (expectedProcessInstanceId != null && 
!expectedProcessInstanceId.isBlank()
+                        && !containsWorkflowSpanTag(trace, 
"sonataflow.process.instance.id",
+                                expectedProcessInstanceId)) {
+                    continue;
+                }
+
+                // If states are required, check that the trace contains them
+                if (requiredStates != null && requiredStates.length > 0) {
+                    Set<String> states = collectWorkflowStates(trace);
+                    boolean allPresent = true;
+                    for (String s : requiredStates) {
+                        if (!states.contains(s)) {
+                            allPresent = false;
+                            break;
+                        }
+                    }
+                    if (!allPresent) {
+                        continue;
+                    }
+                }
+
+                debugPrintTrace(trace);
+                return trace;
+            }
+            return null;
+        }, t -> t != null);
+    }
+
+    private static boolean containsWorkflowSpanTag(JsonNode trace, String key, 
String expectedValue) {
+        JsonNode spans = trace.get("spans");
+        if (spans == null || !spans.isArray()) {
+            return false;
+        }
+
+        for (JsonNode span : spans) {
+            String op = span.hasNonNull("operationName") ? 
span.get("operationName").asText() : "";
+            if (!op.startsWith("sonataflow.process.")) {
+                continue;
+            }
+            JsonNode tags = span.get("tags");
+            if (tags == null || !tags.isArray()) {
+                continue;
+            }
+            for (JsonNode tag : tags) {
+                String k = tag.hasNonNull("key") ? tag.get("key").asText() : 
"";
+                if (!key.equals(k))
+                    continue;
+                String v = tag.hasNonNull("value") ? tag.get("value").asText() 
: "";
+                if (expectedValue.equals(v))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    private static String getJaegerBaseUrl() {
+        String url = System.getProperty(JAEGER_QUERY_BASE_URL_KEY);
+        if (url == null || url.isBlank()) {
+            url = System.getenv("TEST_JAEGER_QUERY_BASE_URL");
+        }
+        if (url == null || url.isBlank()) {
+            throw new IllegalStateException(
+                    "Jaeger base URL not configured. Expected system property: 
" + JAEGER_QUERY_BASE_URL_KEY);
+        }
+        return url;
+    }
+
+    private static String assertAnyWorkflowSpanHasTag(JsonNode trace, String 
key, String expectedValue,
+            boolean checkValue) {
+        JsonNode spans = trace.get("spans");
+        if (spans == null || !spans.isArray()) {
+            throw new AssertionError("Trace JSON missing spans array");
+        }
+
+        for (JsonNode span : spans) {
+            String op = span.hasNonNull("operationName") ? 
span.get("operationName").asText() : "";
+
+            if (!op.startsWith("sonataflow.process.")) {
+                continue;
+            }
+
+            JsonNode tags = span.get("tags");
+            if (tags == null || !tags.isArray()) {
+                continue;
+            }
+
+            for (JsonNode tag : tags) {
+                String k = tag.hasNonNull("key") ? tag.get("key").asText() : 
"";
+                if (!key.equals(k)) {
+                    continue;
+                }
+
+                String actualValue = tag.hasNonNull("value") ? 
tag.get("value").asText() : null;
+
+                if (!checkValue) {
+                    if (actualValue == null || actualValue.isBlank()) {
+                        throw new AssertionError("Tag '" + key + "' found but 
value is empty");
+                    }
+                    return actualValue;
+                }
+
+                if (expectedValue.equals(actualValue)) {
+                    return actualValue;
+                }
+            }
+        }
+
+        if (checkValue) {
+            throw new AssertionError("Did not find workflow span tag '" + key 
+ "' with value '" + expectedValue + "'");
+        }
+
+        throw new AssertionError("Did not find workflow span tag '" + key + "' 
on any workflow span");
+    }
+
+    private static boolean spanHasTag(JsonNode span, String expectedKey, 
String expectedValue) {
+        JsonNode tags = span.get("tags");
+        if (tags == null || !tags.isArray()) {
+            return false;
+        }
+        for (JsonNode tag : tags) {
+            String key = tag.hasNonNull("key") ? tag.get("key").asText() : "";
+            if (!expectedKey.equals(key)) {
+                continue;
+            }
+            String val = tag.hasNonNull("value") ? tag.get("value").asText() : 
"";
+            if (expectedValue == null || expectedValue.equals(val)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isJaegerDebugEnabled() {
+        // -Dtest.jaeger.debug=true
+        String v = System.getProperty(DEBUG_JAEGER_PROP);
+        if (v == null || v.isBlank()) {
+            v = System.getenv("TEST_JAEGER_DEBUG");
+        }
+        return "true".equalsIgnoreCase(v) || "1".equals(v) || 
"yes".equalsIgnoreCase(v);
+    }
+
+    public static void debugPrintTrace(JsonNode trace) {
+        if (!isJaegerDebugEnabled()) {
+            return;
+        }
+
+        LOGGER.info("=== Span names ===");
+        for (JsonNode span : trace.get("spans")) {
+            LOGGER.info("span.operationName=" + 
span.get("operationName").asText());
+        }
+
+        LOGGER.info("=== Tags containing 
process/sonataflow/transaction/tracker ===");
+        for (JsonNode span : trace.get("spans")) {
+            JsonNode tags = span.get("tags");
+            if (tags == null || !tags.isArray()) {
+                continue;
+            }
+
+            String op = span.hasNonNull("operationName") ? 
span.get("operationName").asText() : "<unknown>";
+            for (JsonNode tag : tags) {
+                String key = tag.hasNonNull("key") ? tag.get("key").asText() : 
"";
+                if (key.contains("process") || key.contains("sonataflow") || 
key.contains("transaction")
+                        || key.contains("tracker")) {
+                    LOGGER.info(op + " :: " + tag.toPrettyString());
+                }
+            }
+        }
+    }
+
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerPoller.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerPoller.java
new file mode 100644
index 000000000..968632fbf
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerPoller.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.helper;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Simple polling utilities to avoid sleeps in tests and to handle eventual
+ * consistency in Jaeger.
+ */
+public final class JaegerPoller {
+
+    private JaegerPoller() {
+    }
+
+    public static void waitForService(JaegerQueryClient jaeger, String 
serviceName, Duration timeout,
+            Duration pollInterval) {
+        Instant deadline = Instant.now().plus(timeout);
+
+        while (Instant.now().isBefore(deadline)) {
+            List<String> services = jaeger.listServices();
+            if (services.contains(serviceName)) {
+                return;
+            }
+            sleep(pollInterval);
+        }
+        throw new AssertionError(
+                "Timed out waiting for Jaeger service '" + serviceName + "' to 
appear in /api/services");
+    }
+
+    /**
+     * Waits until Jaeger returns a trace ID for the given service.
+     *
+     * @return the traceId
+     */
+    public static String waitForAnyTrace(JaegerQueryClient jaeger, String 
serviceName, int limit, Duration timeout,
+            Duration pollInterval) {
+
+        Instant deadline = Instant.now().plus(timeout);
+
+        while (Instant.now().isBefore(deadline)) {
+            Optional<String> traceId = 
jaeger.findLatestTraceIdForService(serviceName, limit);
+            if (traceId.isPresent()) {
+                return traceId.get();
+            }
+            sleep(pollInterval);
+        }
+        throw new AssertionError("Timed out waiting for any trace for service 
'" + serviceName + "' in /api/traces");
+    }
+
+    private static void sleep(Duration d) {
+        try {
+            Thread.sleep(Math.max(1, d.toMillis()));
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException("Polling interrupted", e);
+        }
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerQueryClient.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerQueryClient.java
new file mode 100644
index 000000000..febf8daa4
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/helper/JaegerQueryClient.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.helper;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Minimal Jaeger Query HTTP API client for tests. 
+ * Endpoints used: 
+ * - GET /api/services 
+ * - GET /api/traces?service=<service>&limit=<n> 
+ * - GET /api/traces/<traceId>
+ */
+public class JaegerQueryClient {
+
+    private final String baseUrl; 
+    private final HttpClient httpClient;
+    private final ObjectMapper mapper;
+
+    public JaegerQueryClient(String baseUrl) {
+        this.baseUrl = normalizeBaseUrl(baseUrl);
+        this.httpClient = 
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
+        this.mapper = new ObjectMapper();
+    }
+
+    public List<String> listServices() {
+        JsonNode root = getJson("/api/services");
+        JsonNode data = root.get("data");
+        List<String> services = new ArrayList<>();
+        if (data != null && data.isArray()) {
+            data.forEach(n -> services.add(n.asText()));
+        }
+        return services;
+    }
+
+    /**
+     * Find traces for a service using Jaeger's HTTP query API. 
+     */
+    public JsonNode findTracesByService(String serviceName, int limit) {
+        return findTracesByService(serviceName, limit, "1h");
+    }
+
+    /**
+     * Same as above but configurable lookback
+     */
+    public JsonNode findTracesByService(String serviceName, int limit, String 
lookback) {
+        String serviceEnc = URLEncoder.encode(serviceName, 
StandardCharsets.UTF_8);
+        String lookbackEnc = URLEncoder.encode(lookback, 
StandardCharsets.UTF_8);
+
+        // Jaeger supports: service, limit, lookback.
+        // You can also add start/end, tags, operation, minDuration, 
maxDuration if needed.
+        String url = baseUrl + "/api/traces?service=" + serviceEnc + "&limit=" 
+ limit + "&lookback=" + lookbackEnc;
+
+        HttpRequest req = 
HttpRequest.newBuilder().uri(URI.create(url)).GET().header("Accept", 
"application/json")
+                .build();
+
+        HttpResponse<String> resp = send(req);
+
+        if (resp.statusCode() < 200 || resp.statusCode() >= 300) {
+            throw new IllegalStateException("Jaeger findTracesByService 
failed: HTTP " + resp.statusCode() + " url="
+                    + url + " body=" + safeBody(resp.body()));
+        }
+
+        try {
+            return mapper.readTree(resp.body());
+        } catch (IOException e) {
+            throw new IllegalStateException("Failed to parse Jaeger response 
JSON from " + url + ": " + e.getMessage(),
+                    e);
+        }
+    }
+
+    public List<String> findTraceIdsForService(String serviceName, int limit) {
+        JsonNode resp = findTracesByService(serviceName, limit); // implement 
using your existing HTTP call
+        JsonNode data = resp.get("data");
+        if (data == null || !data.isArray() || data.isEmpty()) {
+            return List.of();
+        }
+
+        List<String> ids = new ArrayList<>();
+        for (JsonNode item : data) {
+            JsonNode traceID = item.get("traceID");
+            if (traceID != null && !traceID.asText().isBlank()) {
+                ids.add(traceID.asText());
+            }
+        }
+        return ids;
+    }
+
+    public Optional<String> findLatestTraceIdForService(String serviceName, 
int limit) {
+        List<String> ids = findTraceIdsForService(serviceName, limit);
+        return ids.isEmpty() ? Optional.empty() : Optional.of(ids.get(0));
+    }
+
+    public JsonNode getTrace(String traceId) {
+        return getJson("/api/traces/" + urlEncode(traceId));
+    }
+
+    private JsonNode getJson(String path) {
+        try {
+            HttpRequest req = HttpRequest.newBuilder().uri(URI.create(baseUrl 
+ path)).timeout(Duration.ofSeconds(10))
+                    .GET().build();
+            HttpResponse<String> resp = httpClient.send(req, 
HttpResponse.BodyHandlers.ofString());
+
+            if (resp.statusCode() != 200) {
+                throw new IllegalStateException("Jaeger API call failed: " + 
path + " status=" + resp.statusCode()
+                        + " body=" + truncate(resp.body(), 400));
+            }
+            return mapper.readTree(resp.body());
+        } catch (IOException | InterruptedException e) {
+            throw new RuntimeException("Jaeger API call failed: " + path, e);
+        }
+    }
+
+    private static String normalizeBaseUrl(String baseUrl) {
+        if (baseUrl == null || baseUrl.isBlank()) {
+            throw new IllegalArgumentException("baseUrl must not be 
null/blank");
+        }
+        return baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 
1) : baseUrl;
+    }
+
+    private static String urlEncode(String s) {
+        return URLEncoder.encode(s, StandardCharsets.UTF_8);
+    }
+
+    private static String truncate(String s, int max) {
+        if (s == null) {
+            return "";
+        }
+        return s.length() <= max ? s : s.substring(0, max) + "...";
+    }
+
+    private HttpResponse<String> send(HttpRequest req) {
+        try {
+            return httpClient.send(req, HttpResponse.BodyHandlers.ofString());
+        } catch (IOException | InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException("Failed calling Jaeger: " + 
req.uri() + " -> " + e.getMessage(), e);
+        }
+    }
+
+    private static String safeBody(String body) {
+        if (body == null) {
+            return "";
+        }
+        return body.length() > 500 ? body.substring(0, 500) + "..." : body;
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/CloudEvents.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/CloudEvents.java
new file mode 100644
index 000000000..b6cff6c0f
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/CloudEvents.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.persistence;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public final class CloudEvents {
+
+    private CloudEvents() {
+    }
+
+    public static Map<String, Object> resumeEventWithProcessInstanceId(String 
pid) {
+        Map<String, Object> evt = new HashMap<>();
+        evt.put("specversion", "1.0");
+        evt.put("id", "evt-" + UUID.randomUUID());
+        evt.put("source", "tests");
+        evt.put("type", "resume");
+        evt.put("datacontenttype", "application/json");
+
+        // CloudEvent extension used by SonataFlow callback correlation
+        evt.put("kogitoprocrefid", pid);
+
+        evt.put("data", Map.of());
+        return evt;
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWait01StartIT.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWait01StartIT.java
new file mode 100644
index 000000000..7cc5a75db
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWait01StartIT.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.persistence;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.is;
+import static 
org.kie.kogito.examples.sw.opentelemetry.jaeger.helper.JaegerHelper.*;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import org.kie.kogito.examples.sw.opentelemetry.jaeger.JaegerTestResource;
+
+@QuarkusIntegrationTest
+@QuarkusTestResource(PostgresTestResource.class)
+@QuarkusTestResource(JaegerTestResource.class)
+public class PersistWait01StartIT {
+
+    private static final String START_ENDPOINT = "/persistWait";
+
+    @Test
+    void start_and_persist_instanceId() {
+        // Start workflow so it reaches the waiting state
+        JsonNode started = given().header("X-TRANSACTION-ID", 
"txn-persist-001")
+                                  .header("X-TRACKER-correlation_id", 
"corr-persist-001")
+                                 .contentType("application/json")
+                                 .body("{}")
+                          .when()
+                                 .post(START_ENDPOINT)
+                         .then()
+                                 .statusCode(anyOf(is(200), 
is(201))).extract().as(JsonNode.class);
+
+        String pid = started.hasNonNull("id") ? started.get("id").asText() : 
null;
+        if (pid == null || pid.isBlank()) {
+            throw new AssertionError("Start response did not contain 'id'. 
Response: " + started);
+        }
+
+        JsonNode trace = 
waitForTraceWithTracker("sonataflow.tracker.correlation_id", 
"corr-persist-001");
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.process.id", 
"persistWait");
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.process.instance.id", 
pid);
+
+        assertWorkflowHasAnyState(trace, "printWaitMessage");
+
+        // Verify it exists immediately (before restart in the other test)
+        given().when().get(START_ENDPOINT + "/" + pid).then().statusCode(200);
+
+        PersistWaitPidStore.write(pid);
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWait02ResumeIT.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWait02ResumeIT.java
new file mode 100644
index 000000000..4f7c3bbd8
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWait02ResumeIT.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.persistence;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.is;
+
+import java.time.Duration;
+import java.util.Optional;
+
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import org.kie.kogito.examples.sw.opentelemetry.jaeger.JaegerTestResource;
+import org.kie.kogito.examples.sw.opentelemetry.jaeger.helper.JaegerPoller;
+import 
org.kie.kogito.examples.sw.opentelemetry.jaeger.helper.JaegerQueryClient;
+import static 
org.kie.kogito.examples.sw.opentelemetry.jaeger.helper.JaegerHelper.*;
+
+@QuarkusIntegrationTest
+@QuarkusTestResource(PostgresTestResource.class)
+@QuarkusTestResource(JaegerTestResource.class)
+public class PersistWait02ResumeIT {
+
+    private static final String SERVICE_NAME = 
"sw-opentelemetry-jaeger-example";
+    private static final String PROCESS_ID = "persistWait";
+    private static final String BASE = "/persistWait";
+
+    private static final Duration TIMEOUT = Duration.ofSeconds(60);
+    private static final Duration POLL = Duration.ofMillis(500);
+
+    @Test
+    void resume_after_restart_should_continue_and_trace_has_context() {
+        String pid = PersistWaitPidStore.read();
+
+        //instance must still exist
+        given().when()
+                     .get(BASE + "/" + pid)
+              .then()
+                     .statusCode(200);
+
+        // Send resume CloudEvent
+        given().contentType("application/cloudevents+json")
+              .body(CloudEvents.resumeEventWithProcessInstanceId(pid))
+        .when()
+              .post("/resume")
+       .then()
+              .statusCode(is(202));
+
+        // Verify via Jaeger that we got spans for this process instance 
(after resuming)
+        JaegerQueryClient jaeger = new JaegerQueryClient(getJaegerBaseUrl());
+
+        JaegerPoller.waitForService(jaeger, SERVICE_NAME, TIMEOUT, POLL);
+
+        // Poll until a trace appears that contains our processInstanceId tag
+        String traceId = Awaitility.await().atMost(TIMEOUT).pollInterval(POLL)
+                .until(() -> findTraceIdByProcessInstanceId(jaeger, 
SERVICE_NAME, pid), Optional::isPresent).get();
+
+        JsonNode traceResponse = jaeger.getTrace(traceId);
+
+        JsonNode trace = 
waitForTraceWithTracker("sonataflow.tracker.correlation_id", 
"corr-persist-001", pid,
+                "waitForEvent", "finish");
+
+        debugPrintTrace(trace);
+
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.process.id", 
PROCESS_ID);
+        assertAnyWorkflowSpanHasTag(trace, "sonataflow.process.instance.id", 
pid);
+
+        assertWorkflowStatesContainAtLeast(trace, "waitForEvent", "finish");
+    }
+
+    private static Optional<String> 
findTraceIdByProcessInstanceId(JaegerQueryClient jaeger, String serviceName,
+            String pid) {
+        // Read latest traces and scan them for a span tag 
sonataflow.process.instance.id == pid
+        JsonNode traces = jaeger.findTracesByService(serviceName, 20);
+
+        JsonNode data = traces.get("data");
+        if (data == null || !data.isArray() || data.isEmpty()) {
+            return Optional.empty();
+        }
+
+        for (JsonNode t : data) {
+            String traceId = t.hasNonNull("traceID") ? 
t.get("traceID").asText() : null;
+            if (traceId == null || traceId.isBlank()) {
+                continue;
+            }
+
+            JsonNode full = jaeger.getTrace(traceId);
+            JsonNode trace = extractFirstTrace(full);
+            try {
+                assertAnyWorkflowSpanHasTag(trace, 
"sonataflow.process.instance.id", pid);
+                return Optional.of(traceId);
+            } catch (AssertionError e) {
+            }
+
+        }
+        return Optional.empty();
+    }
+
+    private static String getJaegerBaseUrl() {
+        String key = "test.jaeger.query.base-url";
+        String url = System.getProperty(key);
+        if (url == null || url.isBlank()) {
+            url = System.getenv("TEST_JAEGER_QUERY_BASE_URL");
+        }
+        if (url == null || url.isBlank()) {
+            throw new IllegalStateException("Jaeger base URL not configured. 
Expected system property: " + key);
+        }
+        return url;
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWaitPidStore.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWaitPidStore.java
new file mode 100644
index 000000000..829cf1e20
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PersistWaitPidStore.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.persistence;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public final class PersistWaitPidStore {
+
+    private static final Path FILE = Path.of("target", "persistWait.pid");
+
+    private PersistWaitPidStore() {
+    }
+
+    public static void write(String pid) {
+        try {
+            Files.createDirectories(FILE.getParent());
+            Files.writeString(FILE, pid, StandardCharsets.UTF_8);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to write PID file: " + FILE, e);
+        }
+    }
+
+    public static String read() {
+        try {
+            if (!Files.exists(FILE)) {
+                throw new IllegalStateException("PID file not found: " + FILE 
+ ". Did PersistWait01StartIT run?");
+            }
+            return Files.readString(FILE, StandardCharsets.UTF_8).trim();
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to read PID file: " + FILE, e);
+        }
+    }
+}
diff --git 
a/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PostgresTestResource.java
 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PostgresTestResource.java
new file mode 100644
index 000000000..073a592aa
--- /dev/null
+++ 
b/serverless-workflow-examples/serverless-workflow-opentelemetry-jaeger-quarkus/src/test/java/org/kie/kogito/examples/sw/opentelemetry/jaeger/persistence/PostgresTestResource.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.kie.kogito.examples.sw.opentelemetry.jaeger.persistence;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+public class PostgresTestResource implements 
QuarkusTestResourceLifecycleManager {
+
+    private static final String IMAGE = "postgres:16-alpine";
+    private PostgreSQLContainer<?> postgres;
+
+    @Override
+    public Map<String, String> start() {
+        postgres = new 
PostgreSQLContainer<>(IMAGE).withDatabaseName("kogito").withUsername("kogito")
+                .withPassword("kogito");
+        postgres.start();
+
+        Map<String, String> props = new HashMap<>();
+        props.put("quarkus.datasource.db-kind", "postgresql");
+        props.put("quarkus.datasource.jdbc.url", postgres.getJdbcUrl());
+        props.put("quarkus.datasource.username", postgres.getUsername());
+        props.put("quarkus.datasource.password", postgres.getPassword());
+
+        return props;
+    }
+
+    @Override
+    public void stop() {
+        if (postgres != null) {
+            postgres.stop();
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to