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

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


The following commit(s) were added to refs/heads/main by this push:
     new c0bbbb1e3f [kie-issues#1965] Support re-scheduling Process Instances 
and Node Instances SLA timers (#3940)
c0bbbb1e3f is described below

commit c0bbbb1e3fa40db28717c504e5f773758fe288ec
Author: Pere Fernández <[email protected]>
AuthorDate: Fri May 30 10:21:29 2025 +0200

    [kie-issues#1965] Support re-scheduling Process Instances and Node 
Instances SLA timers (#3940)
---
 .../BaseProcessInstanceManagementResource.java     |  26 +++
 .../management/ProcessInstanceManagement.java      |   4 +
 .../kie/kogito/process/management/SlaPayload.java  |  41 +++--
 .../BaseProcessInstanceManagementResourceTest.java |  10 ++
 .../process/event/KogitoProcessEventSupport.java   |   5 +-
 .../main/java/org/kie/kogito/jobs/JobsService.java |   2 +
 .../org/kie/kogito/process/ProcessInstance.java    |   5 +
 .../uow/events/UnitOfWorkProcessEventListener.java |  23 ++-
 .../process/ProcessInstanceNodeEventBody.java      |   2 +
 .../event/process/ProcessInstanceSLAEventBody.java |   3 +-
 .../process/ProcessInstanceStateEventBody.java     |   1 +
 .../ProcessNodeStateChangeDataEventAdapter.java}   |  36 ++---
 .../ProcessStateChangeEventDataEventAdapter.java}  |  37 ++---
 ....kie.kogito.event.impl.adapter.DataEventAdapter |   2 +
 .../services/jobs/impl/InMemoryJobService.java     |  15 +-
 .../event/KogitoProcessEventListenerAdapter.java   |  20 +--
 .../event/KogitoProcessEventSupportImpl.java       |  22 +--
 .../KogitoProcessNodeStateChangeEventImpl.java     |  40 +++++
 .../event/ProcessStateChangeEventImpl.java         |  40 ++---
 .../listeners/RuleAwareProcessEventListener.java   |  17 +-
 .../workflow/instance/impl/NodeInstanceImpl.java   |   5 +
 .../instance/impl/WorkflowProcessInstanceImpl.java |  37 +++++
 .../workflow/instance/node/EventNodeInstance.java  |  18 +++
 .../instance/node/StateBasedNodeInstance.java      |  19 +++
 .../process/impl/AbstractProcessInstance.java      |  26 ++-
 .../AbstractReactiveMessagingJobsService.java      |   6 +
 .../jobs/management/quarkus/VertxJobsService.java  |  17 ++
 .../quarkus/workflows/ProcessManagementIT.java     |  39 +++++
 .../ProcessInstanceManagementResource.java         |  25 ++-
 .../src/main/resources/application.properties      |   5 +-
 .../src/main/resources/timers.bpmn                 | 178 +++++++++++++++++++++
 .../quarkus/ManagementAddOnIT.java                 | 143 ++++++++++++++++-
 .../springboot/SpringRestJobsService.java          |  35 ++++
 .../ProcessInstanceManagementRestController.java   |  20 ++-
 .../springboot/ManagementAddOnTest.java            |  71 ++++++++
 35 files changed, 845 insertions(+), 150 deletions(-)

diff --git 
a/addons/common/process-management/src/main/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResource.java
 
b/addons/common/process-management/src/main/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResource.java
index 5501fc494f..d3dbd70232 100644
--- 
a/addons/common/process-management/src/main/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResource.java
+++ 
b/addons/common/process-management/src/main/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResource.java
@@ -321,4 +321,30 @@ public abstract class 
BaseProcessInstanceManagementResource<T> implements Proces
     protected abstract T badRequestResponse(String message);
 
     protected abstract T notFoundResponse(String message);
+
+    public T doUpdateNodeInstanceSla(String processId, String 
processInstanceId, String nodeInstanceId, SlaPayload sla) {
+        return executeOnProcessInstance(processId, processInstanceId, 
processInstance -> {
+            try {
+                processInstance.updateNodeInstanceSla(nodeInstanceId, 
sla.getExpirationTime());
+                Map<String, Object> message = new HashMap<>();
+                message.put("message", "Node Instance '" + nodeInstanceId + "' 
SLA due date successfully updated");
+                return buildOkResponse(message);
+            } catch (Exception e) {
+                return badRequestResponse(e.getMessage());
+            }
+        });
+    }
+
+    public T doUpdateProcessInstanceSla(String processId, String 
processInstanceId, SlaPayload sla) {
+        return executeOnProcessInstance(processId, processInstanceId, 
processInstance -> {
+            try {
+                
processInstance.updateProcessInstanceSla(sla.getExpirationTime());
+                Map<String, Object> message = new HashMap<>();
+                message.put("message", "Process Instance '" + 
processInstanceId + "' SLA due date successfully updated");
+                return buildOkResponse(message);
+            } catch (Exception e) {
+                return badRequestResponse(e.getMessage());
+            }
+        });
+    }
 }
diff --git 
a/addons/common/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagement.java
 
b/addons/common/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagement.java
index 77ae708789..7f877dc9c6 100644
--- 
a/addons/common/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagement.java
+++ 
b/addons/common/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagement.java
@@ -50,4 +50,8 @@ public interface ProcessInstanceManagement<T> {
 
     T migrateInstance(String processId, String processInstanceId, 
ProcessMigrationSpec migrationSpec);
 
+    T updateNodeInstanceSla(String processId, String processInstanceId, String 
nodeInstanceId, SlaPayload SLAPayload);
+
+    T updateProcessInstanceSla(String processId, String processInstanceId, 
SlaPayload SLAPayload);
+
 }
diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java 
b/addons/common/process-management/src/main/java/org/kie/kogito/process/management/SlaPayload.java
similarity index 52%
copy from api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
copy to 
addons/common/process-management/src/main/java/org/kie/kogito/process/management/SlaPayload.java
index 1271afcc54..92487b3412 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
+++ 
b/addons/common/process-management/src/main/java/org/kie/kogito/process/management/SlaPayload.java
@@ -16,29 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.kie.kogito.jobs;
 
-/**
- * JobsService provides an entry point for working with different types of jobs
- * that are meant by default to run in background.
- *
- */
-public interface JobsService {
+package org.kie.kogito.process.management;
+
+import java.time.ZonedDateTime;
+
+public class SlaPayload {
+    private ZonedDateTime expirationTime;
+
+    public SlaPayload() {
+    }
+
+    public SlaPayload(ZonedDateTime expirationTime) {
+        this.expirationTime = expirationTime;
+    }
 
-    /**
-     * Schedules process job that is responsible for starting new process 
instances
-     * based on the given description.
-     *
-     * @param description defines what kind of process should be started upon 
expiration time
-     * @return returns unique id of the job
-     */
-    String scheduleJob(JobDescription description);
+    public ZonedDateTime getExpirationTime() {
+        return expirationTime;
+    }
 
-    /**
-     * Cancels given job
-     * 
-     * @param id unique id of the job
-     * @return returns true if the cancellation was successful, otherwise false
-     */
-    boolean cancelJob(String id);
+    public void setExpirationTime(ZonedDateTime expirationTime) {
+        this.expirationTime = expirationTime;
+    }
 }
diff --git 
a/addons/common/process-management/src/test/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResourceTest.java
 
b/addons/common/process-management/src/test/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResourceTest.java
index 199d33bd27..d9bdc433a3 100644
--- 
a/addons/common/process-management/src/test/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResourceTest.java
+++ 
b/addons/common/process-management/src/test/java/org/kie/kogito/process/management/BaseProcessInstanceManagementResourceTest.java
@@ -221,6 +221,16 @@ class BaseProcessInstanceManagementResourceTest {
             public Object migrateInstance(String processId, String 
processInstanceId, ProcessMigrationSpec migrationSpec) {
                 return null;
             }
+
+            @Override
+            public Object updateNodeInstanceSla(String processId, String 
processInstanceId, String nodeInstanceId, SlaPayload SLAPayload) {
+                return null;
+            }
+
+            @Override
+            public Object updateProcessInstanceSla(String processId, String 
processInstanceId, SlaPayload SLAPayload) {
+                return null;
+            }
         });
     }
 
diff --git 
a/api/kogito-api/src/main/java/org/kie/kogito/internal/process/event/KogitoProcessEventSupport.java
 
b/api/kogito-api/src/main/java/org/kie/kogito/internal/process/event/KogitoProcessEventSupport.java
index 77565641b0..be913663b8 100644
--- 
a/api/kogito-api/src/main/java/org/kie/kogito/internal/process/event/KogitoProcessEventSupport.java
+++ 
b/api/kogito-api/src/main/java/org/kie/kogito/internal/process/event/KogitoProcessEventSupport.java
@@ -38,6 +38,8 @@ public interface KogitoProcessEventSupport {
 
     void fireAfterProcessCompleted(KogitoProcessInstance instance, KieRuntime 
kruntime);
 
+    void fireOnProcessStateChanged(KogitoProcessInstance instance, KieRuntime 
kruntime);
+
     void fireBeforeNodeTriggered(KogitoNodeInstance nodeInstance, KieRuntime 
kruntime);
 
     void fireAfterNodeTriggered(KogitoNodeInstance nodeInstance, KieRuntime 
kruntime);
@@ -46,6 +48,8 @@ public interface KogitoProcessEventSupport {
 
     void fireAfterNodeLeft(KogitoNodeInstance nodeInstance, KieRuntime 
kruntime);
 
+    void fireOnNodeStateChanged(KogitoNodeInstance nodeInstance, KieRuntime 
kruntime);
+
     void fireBeforeVariableChanged(String id, String instanceId, Object 
oldValue, Object newValue, List<String> tags,
             KogitoProcessInstance processInstance, KogitoNodeInstance 
nodeInstance, KieRuntime kruntime);
 
@@ -77,5 +81,4 @@ public interface KogitoProcessEventSupport {
     void addEventListener(KogitoProcessEventListener listener);
 
     void removeEventListener(KogitoProcessEventListener listener);
-
 }
diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java 
b/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
index 1271afcc54..be2b2a033a 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
+++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
@@ -41,4 +41,6 @@ public interface JobsService {
      * @return returns true if the cancellation was successful, otherwise false
      */
     boolean cancelJob(String id);
+
+    String rescheduleJob(JobDescription jobDescription);
 }
diff --git 
a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstance.java 
b/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstance.java
index 9c5cad75ed..92229a4968 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstance.java
+++ b/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstance.java
@@ -18,6 +18,7 @@
  */
 package org.kie.kogito.process;
 
+import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
@@ -289,4 +290,8 @@ public interface ProcessInstance<T> {
     long version();
 
     Optional<? extends Correlation<?>> correlation();
+
+    void updateNodeInstanceSla(String nodeInstanceId, ZonedDateTime 
slaDueDate);
+
+    void updateProcessInstanceSla(ZonedDateTime slaDueDate);
 }
diff --git 
a/api/kogito-api/src/main/java/org/kie/kogito/uow/events/UnitOfWorkProcessEventListener.java
 
b/api/kogito-api/src/main/java/org/kie/kogito/uow/events/UnitOfWorkProcessEventListener.java
index ddc09ec964..7d33accfe7 100644
--- 
a/api/kogito-api/src/main/java/org/kie/kogito/uow/events/UnitOfWorkProcessEventListener.java
+++ 
b/api/kogito-api/src/main/java/org/kie/kogito/uow/events/UnitOfWorkProcessEventListener.java
@@ -18,18 +18,7 @@
  */
 package org.kie.kogito.uow.events;
 
-import org.kie.api.event.process.ErrorEvent;
-import org.kie.api.event.process.MessageEvent;
-import org.kie.api.event.process.ProcessCompletedEvent;
-import org.kie.api.event.process.ProcessEvent;
-import org.kie.api.event.process.ProcessMigrationEvent;
-import org.kie.api.event.process.ProcessNodeLeftEvent;
-import org.kie.api.event.process.ProcessNodeTriggeredEvent;
-import org.kie.api.event.process.ProcessRetriggeredEvent;
-import org.kie.api.event.process.ProcessStartedEvent;
-import org.kie.api.event.process.ProcessVariableChangedEvent;
-import org.kie.api.event.process.SLAViolatedEvent;
-import org.kie.api.event.process.SignalEvent;
+import org.kie.api.event.process.*;
 import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener;
 import org.kie.kogito.internal.process.event.ProcessWorkItemTransitionEvent;
 import org.kie.kogito.uow.UnitOfWorkManager;
@@ -67,6 +56,16 @@ public class UnitOfWorkProcessEventListener extends 
DefaultKogitoProcessEventLis
         intercept(event);
     }
 
+    @Override
+    public void onProcessStateChanged(ProcessStateChangeEvent event) {
+        intercept(event);
+    }
+
+    @Override
+    public void onNodeStateChanged(ProcessNodeStateChangeEvent event) {
+        intercept(event);
+    }
+
     @Override
     public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) {
         intercept(event);
diff --git 
a/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceNodeEventBody.java
 
b/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceNodeEventBody.java
index a857b94626..fe0b50896e 100644
--- 
a/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceNodeEventBody.java
+++ 
b/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceNodeEventBody.java
@@ -53,6 +53,8 @@ public class ProcessInstanceNodeEventBody implements 
KogitoMarshallEventSupport,
 
     public static final int EVENT_TYPE_ERROR = 6;
 
+    public static final int EVENT_TYPE_UPDATED = 7;
+
     // common fields for events
 
     private Date eventDate;
diff --git 
a/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceSLAEventBody.java
 
b/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceSLAEventBody.java
index 7801aa094d..1c9d74865e 100644
--- 
a/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceSLAEventBody.java
+++ 
b/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceSLAEventBody.java
@@ -133,7 +133,8 @@ public class ProcessInstanceSLAEventBody implements 
KogitoMarshallEventSupport,
 
     @Override
     public String toString() {
-        return "ProcessErrorEventBody [processId=" + processId + ", 
processInstanceId=" + processInstanceId + ", nodeDefinitionId=" + 
nodeDefinitionId + ", nodeInstanceId=" + nodeInstanceId + "]";
+        return "ProcessInstanceSLAEventBody [processId=" + processId + ", 
processInstanceId=" + processInstanceId + ", nodeDefinitionId=" + 
nodeDefinitionId + ", nodeInstanceId=" + nodeInstanceId
+                + "]";
     }
 
     public static Builder create() {
diff --git 
a/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceStateEventBody.java
 
b/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceStateEventBody.java
index 12e08a56af..aab90cd191 100644
--- 
a/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceStateEventBody.java
+++ 
b/api/kogito-events-api/src/main/java/org/kie/kogito/event/process/ProcessInstanceStateEventBody.java
@@ -46,6 +46,7 @@ public class ProcessInstanceStateEventBody implements 
KogitoMarshallEventSupport
     public static final int EVENT_TYPE_ENDED = 2;
     public static final int EVENT_TYPE_MIGRATED = 3;
     public static final int EVENT_TYPE_RETRIGGERED = 4;
+    public static final int EVENT_TYPE_UPDATED = 5;
 
     // common fields for events
     private Date eventDate;
diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java 
b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/ProcessNodeStateChangeDataEventAdapter.java
similarity index 52%
copy from api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
copy to 
api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/ProcessNodeStateChangeDataEventAdapter.java
index 1271afcc54..21571498b1 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
+++ 
b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/ProcessNodeStateChangeDataEventAdapter.java
@@ -16,29 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.kie.kogito.jobs;
 
-/**
- * JobsService provides an entry point for working with different types of jobs
- * that are meant by default to run in background.
- *
- */
-public interface JobsService {
+package org.kie.kogito.event.impl.adapter;
+
+import org.kie.api.event.process.ProcessNodeStateChangeEvent;
+import org.kie.kogito.event.DataEvent;
+import org.kie.kogito.event.process.ProcessInstanceNodeEventBody;
+
+public class ProcessNodeStateChangeDataEventAdapter extends 
AbstractDataEventAdapter {
 
-    /**
-     * Schedules process job that is responsible for starting new process 
instances
-     * based on the given description.
-     *
-     * @param description defines what kind of process should be started upon 
expiration time
-     * @return returns unique id of the job
-     */
-    String scheduleJob(JobDescription description);
+    public ProcessNodeStateChangeDataEventAdapter() {
+        super(ProcessNodeStateChangeEvent.class);
+    }
 
-    /**
-     * Cancels given job
-     * 
-     * @param id unique id of the job
-     * @return returns true if the cancellation was successful, otherwise false
-     */
-    boolean cancelJob(String id);
+    @Override
+    public DataEvent<?> adapt(Object payload) {
+        return toProcessInstanceNodeEvent((ProcessNodeStateChangeEvent) 
payload, ProcessInstanceNodeEventBody.EVENT_TYPE_UPDATED);
+    }
 }
diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java 
b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/ProcessStateChangeEventDataEventAdapter.java
similarity index 51%
copy from api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
copy to 
api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/ProcessStateChangeEventDataEventAdapter.java
index 1271afcc54..198ada4dd6 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
+++ 
b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/ProcessStateChangeEventDataEventAdapter.java
@@ -16,29 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.kie.kogito.jobs;
+package org.kie.kogito.event.impl.adapter;
 
-/**
- * JobsService provides an entry point for working with different types of jobs
- * that are meant by default to run in background.
- *
- */
-public interface JobsService {
+import org.kie.api.event.process.ProcessStateChangeEvent;
+import org.kie.kogito.event.DataEvent;
+import org.kie.kogito.event.process.ProcessInstanceStateEventBody;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+
+public class ProcessStateChangeEventDataEventAdapter extends 
AbstractDataEventAdapter {
 
-    /**
-     * Schedules process job that is responsible for starting new process 
instances
-     * based on the given description.
-     *
-     * @param description defines what kind of process should be started upon 
expiration time
-     * @return returns unique id of the job
-     */
-    String scheduleJob(JobDescription description);
+    public ProcessStateChangeEventDataEventAdapter() {
+        super(ProcessStateChangeEvent.class);
+    }
 
-    /**
-     * Cancels given job
-     * 
-     * @param id unique id of the job
-     * @return returns true if the cancellation was successful, otherwise false
-     */
-    boolean cancelJob(String id);
+    @Override
+    public DataEvent<?> adapt(Object payload) {
+        ProcessStateChangeEvent event = (ProcessStateChangeEvent) payload;
+        return adapt(event, ProcessInstanceStateEventBody.EVENT_TYPE_UPDATED, 
((KogitoProcessInstance) event.getProcessInstance()).getStartDate());
+    }
 }
diff --git 
a/api/kogito-events-core/src/main/resources/META-INF/services/org.kie.kogito.event.impl.adapter.DataEventAdapter
 
b/api/kogito-events-core/src/main/resources/META-INF/services/org.kie.kogito.event.impl.adapter.DataEventAdapter
index 06682d2352..62c45d852e 100644
--- 
a/api/kogito-events-core/src/main/resources/META-INF/services/org.kie.kogito.event.impl.adapter.DataEventAdapter
+++ 
b/api/kogito-events-core/src/main/resources/META-INF/services/org.kie.kogito.event.impl.adapter.DataEventAdapter
@@ -3,7 +3,9 @@ 
org.kie.kogito.event.impl.adapter.ProcessErrorEventDataEventAdapter
 org.kie.kogito.event.impl.adapter.ProcessMigratedEventDataEventAdapter
 org.kie.kogito.event.impl.adapter.ProcessNodeEnteredEventDataEventAdapter
 org.kie.kogito.event.impl.adapter.ProcessNodeLeftEventDataEventAdapter
+org.kie.kogito.event.impl.adapter.ProcessNodeStateChangeDataEventAdapter
 org.kie.kogito.event.impl.adapter.ProcessSLAEventDataEventAdapter
+org.kie.kogito.event.impl.adapter.ProcessStateChangeEventDataEventAdapter
 org.kie.kogito.event.impl.adapter.ProcessStartedEventDataEventAdapter
 org.kie.kogito.event.impl.adapter.ProcessRetriggeredEventDataEventAdapter
 org.kie.kogito.event.impl.adapter.ProcessVariableEventDataEventAdapter
diff --git 
a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java
 
b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java
index f48f8b9c2b..fb6fd55e96 100644
--- 
a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java
+++ 
b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java
@@ -24,11 +24,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
 
 import org.kie.kogito.jobs.JobDescription;
 import org.kie.kogito.jobs.JobsService;
@@ -110,6 +106,15 @@ public class InMemoryJobService implements JobsService, 
AutoCloseable {
         return false;
     }
 
+    @Override
+    public String rescheduleJob(JobDescription jobDescription) {
+        LOGGER.debug("Reschedule Job: {}", jobDescription.id());
+        if (cancelJob(jobDescription.id())) {
+            return scheduleJob(jobDescription);
+        }
+        return "Job reschedule failed";
+    }
+
     protected long calculateDelay(JobDescription description) {
         long delay = Duration.between(ZonedDateTime.now(), 
description.expirationTime().get()).toMillis();
         if (delay <= 0) {
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventListenerAdapter.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventListenerAdapter.java
index 57fd46e93c..9bc6db62b8 100644
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventListenerAdapter.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventListenerAdapter.java
@@ -18,15 +18,7 @@
  */
 package org.jbpm.process.instance.event;
 
-import org.kie.api.event.process.MessageEvent;
-import org.kie.api.event.process.ProcessCompletedEvent;
-import org.kie.api.event.process.ProcessEventListener;
-import org.kie.api.event.process.ProcessNodeLeftEvent;
-import org.kie.api.event.process.ProcessNodeTriggeredEvent;
-import org.kie.api.event.process.ProcessStartedEvent;
-import org.kie.api.event.process.ProcessVariableChangedEvent;
-import org.kie.api.event.process.SLAViolatedEvent;
-import org.kie.api.event.process.SignalEvent;
+import org.kie.api.event.process.*;
 import org.kie.kogito.internal.process.event.KogitoProcessEventListener;
 
 public class KogitoProcessEventListenerAdapter implements 
KogitoProcessEventListener {
@@ -57,6 +49,11 @@ public class KogitoProcessEventListenerAdapter implements 
KogitoProcessEventList
         delegate.afterProcessCompleted(processCompletedEvent);
     }
 
+    @Override
+    public void onProcessStateChanged(ProcessStateChangeEvent event) {
+        delegate.onProcessStateChanged(event);
+    }
+
     @Override
     public void beforeNodeTriggered(ProcessNodeTriggeredEvent 
processNodeTriggeredEvent) {
         delegate.beforeNodeTriggered(processNodeTriggeredEvent);
@@ -77,6 +74,11 @@ public class KogitoProcessEventListenerAdapter implements 
KogitoProcessEventList
         delegate.afterNodeLeft(processNodeLeftEvent);
     }
 
+    @Override
+    public void onNodeStateChanged(ProcessNodeStateChangeEvent event) {
+        delegate.onNodeStateChanged(event);
+    }
+
     @Override
     public void beforeVariableChanged(ProcessVariableChangedEvent 
processVariableChangedEvent) {
         delegate.beforeVariableChanged(processVariableChangedEvent);
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventSupportImpl.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventSupportImpl.java
index b03d37b55e..f79a6733c9 100644
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventSupportImpl.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessEventSupportImpl.java
@@ -23,15 +23,7 @@ import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Consumer;
 
-import org.kie.api.event.process.MessageEvent;
-import org.kie.api.event.process.ProcessCompletedEvent;
-import org.kie.api.event.process.ProcessNodeLeftEvent;
-import org.kie.api.event.process.ProcessNodeTriggeredEvent;
-import org.kie.api.event.process.ProcessRetriggeredEvent;
-import org.kie.api.event.process.ProcessStartedEvent;
-import org.kie.api.event.process.ProcessVariableChangedEvent;
-import org.kie.api.event.process.SLAViolatedEvent;
-import org.kie.api.event.process.SignalEvent;
+import org.kie.api.event.process.*;
 import org.kie.api.runtime.KieRuntime;
 import org.kie.internal.runtime.Closeable;
 import org.kie.kogito.auth.IdentityProvider;
@@ -110,6 +102,12 @@ public class KogitoProcessEventSupportImpl implements 
KogitoProcessEventSupport
         notifyAllListeners(l -> l.afterProcessCompleted(event));
     }
 
+    @Override
+    public void fireOnProcessStateChanged(KogitoProcessInstance instance, 
KieRuntime kruntime) {
+        final ProcessStateChangeEvent event = new 
ProcessStateChangeEventImpl(instance, kruntime, identityProvider.getName());
+        notifyAllListeners(l -> l.onProcessStateChanged(event));
+    }
+
     @Override
     public void fireBeforeNodeTriggered(final KogitoNodeInstance nodeInstance, 
KieRuntime kruntime) {
         final ProcessNodeTriggeredEvent event = new 
KogitoProcessNodeTriggeredEventImpl(nodeInstance, kruntime, 
identityProvider.getName());
@@ -134,6 +132,12 @@ public class KogitoProcessEventSupportImpl implements 
KogitoProcessEventSupport
         notifyAllListeners(l -> l.afterNodeLeft(event));
     }
 
+    @Override
+    public void fireOnNodeStateChanged(KogitoNodeInstance nodeInstance, 
KieRuntime kruntime) {
+        final ProcessNodeStateChangeEvent event = new 
KogitoProcessNodeStateChangeEventImpl(nodeInstance, kruntime, 
identityProvider.getName());
+        notifyAllListeners(l -> l.onNodeStateChanged(event));
+    }
+
     @Override
     public void fireBeforeVariableChanged(final String id, final String 
instanceId,
             final Object oldValue, final Object newValue,
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessNodeStateChangeEventImpl.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessNodeStateChangeEventImpl.java
new file mode 100644
index 0000000000..8f64e3e4dd
--- /dev/null
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/KogitoProcessNodeStateChangeEventImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.jbpm.process.instance.event;
+
+import org.kie.api.event.process.ProcessNodeStateChangeEvent;
+import org.kie.api.runtime.KieRuntime;
+import org.kie.api.runtime.process.NodeInstance;
+import org.kie.kogito.internal.process.runtime.KogitoNodeInstance;
+
+public class KogitoProcessNodeStateChangeEventImpl extends 
AbstractProcessNodeEvent implements ProcessNodeStateChangeEvent {
+
+    private static final long serialVersionUID = 510l;
+
+    public KogitoProcessNodeStateChangeEventImpl(NodeInstance nodeInstance, 
KieRuntime kruntime, String identity) {
+        super(nodeInstance, nodeInstance.getProcessInstance(), kruntime, 
identity);
+    }
+
+    @Override
+    public String toString() {
+        return "==>[ProcessNodeStateChangeEvent(nodeId=" + 
getNodeInstance().getNodeId() + "; id=" + ((KogitoNodeInstance) 
getNodeInstance()).getStringId()
+                + "; nodeName=" + getNodeInstance().getNodeName() + "; 
processName=" + getProcessInstance().getProcessName() + "; processId=" + 
getProcessInstance().getProcessId() + ")]";
+    }
+}
diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/ProcessStateChangeEventImpl.java
similarity index 52%
copy from api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
copy to 
jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/ProcessStateChangeEventImpl.java
index 1271afcc54..c2c5a72c62 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/ProcessStateChangeEventImpl.java
@@ -16,29 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.kie.kogito.jobs;
+package org.jbpm.process.instance.event;
 
-/**
- * JobsService provides an entry point for working with different types of jobs
- * that are meant by default to run in background.
- *
- */
-public interface JobsService {
+import org.kie.api.event.process.ProcessStateChangeEvent;
+import org.kie.api.runtime.KieRuntime;
+import org.kie.api.runtime.process.ProcessInstance;
+
+public class ProcessStateChangeEventImpl extends ProcessEvent implements 
ProcessStateChangeEvent {
+
+    private static final long serialVersionUID = 510l;
+
+    public ProcessStateChangeEventImpl(ProcessInstance instance, KieRuntime 
kruntime, String identity) {
+        super(instance, kruntime, identity);
+    }
 
-    /**
-     * Schedules process job that is responsible for starting new process 
instances
-     * based on the given description.
-     *
-     * @param description defines what kind of process should be started upon 
expiration time
-     * @return returns unique id of the job
-     */
-    String scheduleJob(JobDescription description);
+    @Override
+    public String toString() {
+        return "==>[ProcessStateChangeEvent(name=" + 
getProcessInstance().getProcessName() + "; id=" + 
getProcessInstance().getProcessId() + ")]";
+    }
 
-    /**
-     * Cancels given job
-     * 
-     * @param id unique id of the job
-     * @return returns true if the cancellation was successful, otherwise false
-     */
-    boolean cancelJob(String id);
-}
+}
\ No newline at end of file
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/listeners/RuleAwareProcessEventListener.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/listeners/RuleAwareProcessEventListener.java
index 6beee073dd..aaa8d34bf9 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/listeners/RuleAwareProcessEventListener.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/event/listeners/RuleAwareProcessEventListener.java
@@ -21,12 +21,7 @@ package org.jbpm.process.instance.event.listeners;
 import java.util.Collection;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.kie.api.event.process.ProcessCompletedEvent;
-import org.kie.api.event.process.ProcessEventListener;
-import org.kie.api.event.process.ProcessNodeLeftEvent;
-import org.kie.api.event.process.ProcessNodeTriggeredEvent;
-import org.kie.api.event.process.ProcessStartedEvent;
-import org.kie.api.event.process.ProcessVariableChangedEvent;
+import org.kie.api.event.process.*;
 import org.kie.api.runtime.KieRuntime;
 import org.kie.api.runtime.process.WorkflowProcessInstance;
 import org.kie.api.runtime.rule.FactHandle;
@@ -78,6 +73,16 @@ public class RuleAwareProcessEventListener implements 
ProcessEventListener {
         // do nothing
     }
 
+    @Override
+    public void onProcessStateChanged(ProcessStateChangeEvent event) {
+        // do nothing
+    }
+
+    @Override
+    public void onNodeStateChanged(ProcessNodeStateChangeEvent event) {
+        // do nothing
+    }
+
     public void afterNodeLeft(ProcessNodeLeftEvent event) {
         // do nothing
     }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
index 90083bf58c..c2bb0054ca 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
@@ -19,6 +19,7 @@
 package org.jbpm.workflow.instance.impl;
 
 import java.io.Serializable;
+import java.time.ZonedDateTime;
 import java.util.*;
 import java.util.Map.Entry;
 import java.util.function.Function;
@@ -631,6 +632,10 @@ public abstract class NodeInstanceImpl implements 
org.jbpm.workflow.instance.Nod
 
     }
 
+    public void rescheduleSlaTimer(ZonedDateTime slaDueDate) {
+        throw new UnsupportedOperationException("Unsupported operation");
+    }
+
     @Override
     public int getSlaCompliance() {
         return slaCompliance;
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
index 8b1d3614a7..f533f71cbb 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
@@ -19,6 +19,7 @@
 package org.jbpm.workflow.instance.impl;
 
 import java.time.Duration;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -96,6 +97,7 @@ import 
org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
 import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess;
 import org.kie.kogito.internal.process.runtime.MessageException;
 import org.kie.kogito.jobs.DurationExpirationTime;
+import org.kie.kogito.jobs.ExactExpirationTime;
 import org.kie.kogito.jobs.JobsService;
 import org.kie.kogito.jobs.TimerDescription;
 import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription;
@@ -113,6 +115,7 @@ import org.mvel2.integration.impl.ImmutableDefaultFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Objects.isNull;
 import static 
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_ENVIRONMENT_KEY;
 import static org.jbpm.ruleflow.core.Metadata.COMPENSATION;
 import static org.jbpm.ruleflow.core.Metadata.CONDITION;
@@ -123,6 +126,7 @@ import static 
org.jbpm.ruleflow.core.Metadata.EVENT_TYPE_SIGNAL;
 import static org.jbpm.ruleflow.core.Metadata.IS_FOR_COMPENSATION;
 import static 
org.jbpm.workflow.instance.impl.DummyEventListener.EMPTY_EVENT_LISTENER;
 import static 
org.jbpm.workflow.instance.node.TimerNodeInstance.TIMER_TRIGGERED_EVENT;
+import static org.kie.kogito.internal.utils.ConversionUtils.isEmpty;
 import static org.kie.kogito.internal.utils.ConversionUtils.isNotEmpty;
 import static org.kie.kogito.process.flexible.ItemDescription.Status.ACTIVE;
 import static org.kie.kogito.process.flexible.ItemDescription.Status.AVAILABLE;
@@ -1449,4 +1453,37 @@ public abstract class WorkflowProcessInstanceImpl 
extends ProcessInstanceImpl im
         return this.kogitoProcessInstance;
     }
 
+    public void rescheduleTimer(String timerId, ZonedDateTime slaDueDate) {
+        rescheduleTimer(timerId, slaDueDate, null);
+    }
+
+    public void rescheduleTimer(String timerId, ZonedDateTime slaDueDate, 
String nodeInstanceId) {
+        ProcessInstanceJobDescription description =
+                
ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder()
+                        .id(timerId)
+                        .timerId("-1")
+                        .expirationTime(ExactExpirationTime.of(slaDueDate))
+                        .processInstanceId(getStringId())
+                        .processId(getProcessId())
+                        .nodeInstanceId(nodeInstanceId)
+                        .rootProcessId(getRootProcessId())
+                        .rootProcessInstanceId(getRootProcessInstanceId())
+                        .build();
+        JobsService jobsService = 
InternalProcessRuntime.asKogitoProcessRuntime(getKnowledgeRuntime().getProcessRuntime()).getJobsService();
+        jobsService.rescheduleJob(description);
+    }
+
+    public void rescheduleSlaTimer(ZonedDateTime slaDueDate) {
+        if (isNull(slaDueDate)) {
+            throw new IllegalArgumentException("Cannot update SLA: slaDueDate 
cannot be null");
+        }
+
+        if (isEmpty(slaTimerId)) {
+            throw new IllegalStateException("Cannot update SLA: Process 
Instance has NO SLA configured");
+        }
+        InternalProcessRuntime processRuntime = ((InternalProcessRuntime) 
getKnowledgeRuntime().getProcessRuntime());
+        rescheduleTimer(slaTimerId, slaDueDate);
+        this.slaDueDate = Date.from(slaDueDate.toInstant());
+        
processRuntime.getProcessEventSupport().fireOnProcessStateChanged(this, 
getKnowledgeRuntime());
+    }
 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/EventNodeInstance.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/EventNodeInstance.java
index 4ad8e4127c..10cc2587e0 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/EventNodeInstance.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/EventNodeInstance.java
@@ -19,6 +19,7 @@
 package org.jbpm.workflow.instance.node;
 
 import java.io.Serializable;
+import java.time.ZonedDateTime;
 import java.util.*;
 import java.util.function.Function;
 import java.util.regex.Matcher;
@@ -42,6 +43,7 @@ import org.kie.kogito.process.EventDescription;
 import org.kie.kogito.process.NamedDataType;
 import org.kie.kogito.timer.TimerInstance;
 
+import static java.util.Objects.isNull;
 import static 
org.jbpm.workflow.instance.impl.DummyEventListener.EMPTY_EVENT_LISTENER;
 import static 
org.jbpm.workflow.instance.node.TimerNodeInstance.TIMER_TRIGGERED_EVENT;
 import static org.kie.kogito.internal.utils.ConversionUtils.isEmpty;
@@ -292,4 +294,20 @@ public class EventNodeInstance extends 
ExtendedNodeInstanceImpl implements Kogit
         toReturn.add(slaTimer);
         return toReturn;
     }
+
+    @Override
+    public void rescheduleSlaTimer(ZonedDateTime slaDueDate) {
+        if (isNull(slaDueDate)) {
+            throw new IllegalArgumentException("Cannot update SLA: slaDueDate 
cannot be null");
+        }
+
+        if (isEmpty(slaTimerId)) {
+            throw new IllegalStateException("Cannot update SLA: Node has NO 
SLA configured");
+        }
+        InternalProcessRuntime processRuntime = ((InternalProcessRuntime) 
getProcessInstance().getKnowledgeRuntime().getProcessRuntime());
+        ((WorkflowProcessInstanceImpl) 
getProcessInstance()).rescheduleTimer(slaTimerId, slaDueDate, getId());
+        this.slaDueDate = Date.from(slaDueDate.toInstant());
+        processRuntime.getProcessEventSupport().fireOnNodeStateChanged(this, 
getProcessInstance().getKnowledgeRuntime());
+    }
+
 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
index 2aee1f690e..fab2a6bf67 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
@@ -18,6 +18,7 @@
  */
 package org.jbpm.workflow.instance.node;
 
+import java.time.ZonedDateTime;
 import java.util.*;
 
 import org.drools.core.common.InternalAgenda;
@@ -51,9 +52,11 @@ import org.kie.kogito.timer.TimerInstance;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Objects.isNull;
 import static 
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_ENVIRONMENT_KEY;
 import static org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE;
 import static 
org.jbpm.workflow.instance.node.TimerNodeInstance.TIMER_TRIGGERED_EVENT;
+import static org.kie.kogito.internal.utils.ConversionUtils.isEmpty;
 import static org.kie.kogito.internal.utils.ConversionUtils.isNotEmpty;
 
 public abstract class StateBasedNodeInstance extends ExtendedNodeInstanceImpl 
implements EventBasedNodeInstanceInterface, KogitoEventListener {
@@ -494,4 +497,20 @@ public abstract class StateBasedNodeInstance extends 
ExtendedNodeInstanceImpl im
 
         return toReturn;
     }
+
+    @Override
+    public void rescheduleSlaTimer(ZonedDateTime slaDueDate) {
+        if (isNull(slaDueDate)) {
+            throw new IllegalArgumentException("Cannot update SLA: slaDueDate 
cannot be null");
+        }
+
+        if (isEmpty(slaTimerId)) {
+            throw new IllegalStateException("Cannot update SLA: Node has NO 
SLA configured");
+        }
+
+        InternalProcessRuntime processRuntime = ((InternalProcessRuntime) 
getProcessInstance().getKnowledgeRuntime().getProcessRuntime());
+        ((WorkflowProcessInstanceImpl) 
getProcessInstance()).rescheduleTimer(slaTimerId, slaDueDate, getId());
+        this.slaDueDate = Date.from(slaDueDate.toInstant());
+        processRuntime.getProcessEventSupport().fireOnNodeStateChanged(this, 
getProcessInstance().getKnowledgeRuntime());
+    }
 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessInstance.java
 
b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessInstance.java
index 73a03b3006..13c9aac1c5 100644
--- 
a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessInstance.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessInstance.java
@@ -19,6 +19,7 @@
 package org.kie.kogito.process.impl;
 
 import java.lang.reflect.Field;
+import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -511,7 +512,6 @@ public abstract class AbstractProcessInstance<T extends 
Model> implements Proces
                     .filter(ni -> ni.getStringId().equals(nodeInstanceId))
                     .findFirst()
                     .orElseThrow(() -> new 
NodeInstanceNotFoundException(this.id, nodeInstanceId));
-
             nodeInstance.cancel();
             return null;
         });
@@ -531,6 +531,27 @@ public abstract class AbstractProcessInstance<T extends 
Model> implements Proces
         });
     }
 
+    @Override
+    public void updateNodeInstanceSla(String nodeInstanceId, ZonedDateTime 
slaDueDate) {
+        executeInWorkflowProcessInstanceWrite(pi -> {
+            NodeInstance nodeInstance = pi.getNodeInstances(true)
+                    .stream()
+                    .filter(ni -> ni.getId().equals(nodeInstanceId))
+                    .findFirst()
+                    .orElseThrow(() -> new 
NodeInstanceNotFoundException(this.id, nodeInstanceId));
+            ((NodeInstanceImpl) nodeInstance).rescheduleSlaTimer(slaDueDate);
+            return null;
+        });
+    }
+
+    @Override
+    public void updateProcessInstanceSla(ZonedDateTime slaDueDate) {
+        executeInWorkflowProcessInstanceWrite(pi -> {
+            pi.rescheduleSlaTimer(slaDueDate);
+            return null;
+        });
+    }
+
     public <R> R 
executeInWorkflowProcessInstanceWrite(Function<WorkflowProcessInstanceImpl, R> 
execution) {
         checkWriteOnly();
         return executeInWorkflowProcessInstance(execution);
@@ -545,7 +566,7 @@ public abstract class AbstractProcessInstance<T extends 
Model> implements Proces
     /**
      * this is intended to be used internal. Sometimes is required to perform 
low level operations that require some
      * internal state of the process like obtaining the SLA or operating nodes 
instances.
-     * 
+     *
      * @param <R>
      * @param execution
      * @return
@@ -667,7 +688,6 @@ public abstract class AbstractProcessInstance<T extends 
Model> implements Proces
     }
 
     @Override
-
     public void transitionWorkItem(String workItemId, WorkItemTransition 
transition) {
         executeInWorkflowProcessInstanceWrite(pi -> {
             
getProcessRuntime().getKogitoProcessRuntime().getKogitoWorkItemManager().transitionWorkItem(workItemId,
 transition);
diff --git 
a/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java
 
b/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java
index 3f61e1fe1d..84e2c81ef1 100644
--- 
a/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java
+++ 
b/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java
@@ -94,6 +94,12 @@ public abstract class AbstractReactiveMessagingJobsService 
implements JobsServic
         return true;
     }
 
+    @Override
+    public String rescheduleJob(JobDescription jobDescription) {
+        cancelJob(jobDescription.id());
+        return scheduleJob(jobDescription);
+    }
+
     protected Message<String> decorate(Message<String> message) {
         return message;
     }
diff --git 
a/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java
 
b/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java
index e5be0229b6..d90a8feedc 100644
--- 
a/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java
+++ 
b/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java
@@ -116,6 +116,23 @@ public class VertxJobsService extends RestJobsService {
         return true;
     }
 
+    @Override
+    public String rescheduleJob(JobDescription jobDescription) {
+        String callback = getCallbackEndpoint(jobDescription);
+        LOGGER.debug("Job to be rescheduled {} with callback URL {}", 
jobDescription, callback);
+        final Job job = buildJob(jobDescription, callback);
+        client.patch(JOBS_PATH).sendJson(job, res -> {
+            int status = res.result() != null ? res.result().statusCode() : 0;
+            if (res.succeeded() && status == 200) {
+                LOGGER.debug("Creating of the job {} done with status code {} 
", job, status);
+                System.out.println(res.result().bodyAsString());
+            } else {
+                LOGGER.error("Scheduling of job {} failed with response code 
{}", job, status, res.cause());
+            }
+        });
+        return "Job Rescheduled";
+    }
+
     private void configureMapper(ObjectMapper mapper) {
         
mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
         mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
diff --git 
a/quarkus/addons/process-management/integration-tests/src/test/java/org/kie/kogito/quarkus/workflows/ProcessManagementIT.java
 
b/quarkus/addons/process-management/integration-tests/src/test/java/org/kie/kogito/quarkus/workflows/ProcessManagementIT.java
index 9d83bbc17e..21183855ed 100644
--- 
a/quarkus/addons/process-management/integration-tests/src/test/java/org/kie/kogito/quarkus/workflows/ProcessManagementIT.java
+++ 
b/quarkus/addons/process-management/integration-tests/src/test/java/org/kie/kogito/quarkus/workflows/ProcessManagementIT.java
@@ -18,9 +18,11 @@
  */
 package org.kie.kogito.quarkus.workflows;
 
+import java.time.ZonedDateTime;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.kie.kogito.process.management.SlaPayload;
 
 import io.quarkus.test.junit.QuarkusIntegrationTest;
 import io.restassured.RestAssured;
@@ -106,4 +108,41 @@ public class ProcessManagementIT {
                         hasKey("timerId"),
                         hasEntry("description", "Task-Boundary Timer"))));
     }
+
+    @Test
+    public void testRescheduleSLATimersEndpoints() {
+        String processInstanceId = given()
+                .contentType(ContentType.JSON)
+                .when()
+                .post("/timers")
+                .then()
+                .statusCode(201)
+                .body("id", notNullValue())
+                .extract().path("id");
+
+        String nodeInstanceId = given()
+                .when()
+                
.get("/management/processes/timers/instances/{processInstanceId}/nodeInstances",
 processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(1))
+                .extract().path("[0].nodeInstanceId");
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                
.patch("/management/processes/timers/instances/{processInstanceId}/sla", 
processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("message", equalTo("Process Instance '" + 
processInstanceId + "' SLA due date successfully updated"));
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                .when()
+                
.patch("/management/processes/timers/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/sla",
 processInstanceId, nodeInstanceId)
+                .then()
+                .statusCode(200)
+                .body("message", equalTo("Node Instance '" + nodeInstanceId + 
"' SLA due date successfully updated"));
+    }
 }
diff --git 
a/quarkus/addons/process-management/runtime/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementResource.java
 
b/quarkus/addons/process-management/runtime/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementResource.java
index 6bf8433f63..9aecfa2ecb 100644
--- 
a/quarkus/addons/process-management/runtime/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementResource.java
+++ 
b/quarkus/addons/process-management/runtime/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementResource.java
@@ -23,12 +23,7 @@ import org.kie.kogito.process.Processes;
 
 import jakarta.enterprise.inject.Instance;
 import jakarta.inject.Inject;
-import jakarta.ws.rs.DELETE;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.POST;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.PathParam;
-import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.*;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Response.Status;
@@ -189,4 +184,22 @@ public class ProcessInstanceManagementResource extends 
BaseProcessInstanceManage
     public Response cancelProcessInstanceId(@PathParam("processId") String 
processId, @PathParam("processInstanceId") String processInstanceId) {
         return doCancelProcessInstanceId(processId, processInstanceId);
     }
+
+    @Override
+    @PATCH
+    
@Path("{processId}/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/sla")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateNodeInstanceSla(@PathParam("processId") String 
processId, @PathParam("processInstanceId") String processInstanceId, 
@PathParam("nodeInstanceId") String nodeInstanceId,
+            SlaPayload slaPayload) {
+        return doUpdateNodeInstanceSla(processId, processInstanceId, 
nodeInstanceId, slaPayload);
+    }
+
+    @Override
+    @PATCH
+    @Path("{processId}/instances/{processInstanceId}/sla")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateProcessInstanceSla(@PathParam("processId") String 
processId, @PathParam("processInstanceId") String processInstanceId, SlaPayload 
slaPayload) {
+        return doUpdateProcessInstanceSla(processId, processInstanceId, 
slaPayload);
+    }
+
 }
diff --git 
a/quarkus/integration-tests/integration-tests-quarkus-processes/src/main/resources/application.properties
 
b/quarkus/integration-tests/integration-tests-quarkus-processes/src/main/resources/application.properties
index 58d7ff6dc2..b3eaaa789f 100644
--- 
a/quarkus/integration-tests/integration-tests-quarkus-processes/src/main/resources/application.properties
+++ 
b/quarkus/integration-tests/integration-tests-quarkus-processes/src/main/resources/application.properties
@@ -53,4 +53,7 @@ quarkus.http.auth.permission.default.policy=authenticated
 
 quarkus.security.users.embedded.enabled=true
 quarkus.security.users.embedded.plain-text=true
-quarkus.security.users.embedded.users.buddy=buddy
\ No newline at end of file
+quarkus.security.users.embedded.users.buddy=buddy
+
+kogito.transactionEnabled=false
+kogito.faultToleranceEnabled=false
\ No newline at end of file
diff --git 
a/quarkus/integration-tests/integration-tests-quarkus-processes/src/main/resources/timers.bpmn
 
b/quarkus/integration-tests/integration-tests-quarkus-processes/src/main/resources/timers.bpmn
new file mode 100644
index 0000000000..63b3bac089
--- /dev/null
+++ 
b/quarkus/integration-tests/integration-tests-quarkus-processes/src/main/resources/timers.bpmn
@@ -0,0 +1,178 @@
+<?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.
+  -->
+<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"; 
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"; 
xmlns:bpsim="http://www.bpsim.org/schemas/1.0"; 
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"; 
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"; 
xmlns:drools="http://www.jboss.org/drools"; id="_FbQn4Bn5ED6qPL2RHaehww" 
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd 
http://www. [...]
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_SkippableInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_PriorityInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_CommentInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_DescriptionInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_CreatedByInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_TaskNameInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_GroupIdInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_ContentInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_NotStartedReassignInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_NotCompletedReassignInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_NotStartedNotifyInputXItem" 
structureRef="Object"/>
+  <bpmn2:itemDefinition 
id="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_NotCompletedNotifyInputXItem" 
structureRef="Object"/>
+  <bpmn2:collaboration id="_11F36E9B-11DF-499D-896C-0D73277FBCB5" 
name="Default Collaboration">
+    <bpmn2:participant id="_3CC3D518-96F1-4246-B77A-BD69EA62333A" name="Pool 
Participant" processRef="timers"/>
+  </bpmn2:collaboration>
+  <bpmn2:process id="timers" drools:packageName="com.example" 
drools:version="1.0" drools:adHoc="false" name="timers" isExecutable="true" 
processType="Public">
+    <bpmn2:extensionElements>
+      <drools:metaData name="customSLADueDate">
+        <drools:metaValue><![CDATA[200m]]></drools:metaValue>
+      </drools:metaData>
+      <drools:metaData name="processDuration">
+        <drools:metaValue><![CDATA[PT1M]]></drools:metaValue>
+      </drools:metaData>
+    </bpmn2:extensionElements>
+    <bpmn2:sequenceFlow id="_2CCE6D96-07C9-4196-A3DA-2FA12675606B" 
sourceRef="_C64FDE83-9AE3-4BC8-94AC-14805BE0D3D3" 
targetRef="_A2B595B9-3809-487E-B354-677DE3EB9B83"/>
+    <bpmn2:sequenceFlow id="_217C7B88-D29D-479F-9DDA-49C505B55DB3" 
sourceRef="_CB75879B-EB0D-4F28-AC61-E6FAA0126490" 
targetRef="_66215F0B-D52C-4DEF-8BAE-3524FD59A246"/>
+    <bpmn2:sequenceFlow id="_BB186F29-4721-4C71-BFAA-D816C47ED670" 
sourceRef="_F35EC805-22D5-4833-B56D-9DE16B4B258B" 
targetRef="_CB75879B-EB0D-4F28-AC61-E6FAA0126490"/>
+    <bpmn2:endEvent id="_A2B595B9-3809-487E-B354-677DE3EB9B83">
+      <bpmn2:incoming>_2CCE6D96-07C9-4196-A3DA-2FA12675606B</bpmn2:incoming>
+    </bpmn2:endEvent>
+    <bpmn2:endEvent id="_66215F0B-D52C-4DEF-8BAE-3524FD59A246">
+      <bpmn2:incoming>_217C7B88-D29D-479F-9DDA-49C505B55DB3</bpmn2:incoming>
+    </bpmn2:endEvent>
+    <bpmn2:userTask id="_CB75879B-EB0D-4F28-AC61-E6FAA0126490" name="Task">
+      <bpmn2:extensionElements>
+        <drools:metaData name="elementname">
+          <drools:metaValue><![CDATA[Task]]></drools:metaValue>
+        </drools:metaData>
+        <drools:metaData name="customSLADueDate">
+          <drools:metaValue><![CDATA[150m]]></drools:metaValue>
+        </drools:metaData>
+      </bpmn2:extensionElements>
+      <bpmn2:incoming>_BB186F29-4721-4C71-BFAA-D816C47ED670</bpmn2:incoming>
+      <bpmn2:outgoing>_217C7B88-D29D-479F-9DDA-49C505B55DB3</bpmn2:outgoing>
+      <bpmn2:ioSpecification>
+        <bpmn2:dataInput 
id="_CB75879B-EB0D-4F28-AC61-E6FAA0126490_TaskNameInputX" drools:dtype="Object" 
itemSubjectRef="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_TaskNameInputXItem" 
name="TaskName"/>
+        <bpmn2:dataInput 
id="_CB75879B-EB0D-4F28-AC61-E6FAA0126490_SkippableInputX" 
drools:dtype="Object" 
itemSubjectRef="__CB75879B-EB0D-4F28-AC61-E6FAA0126490_SkippableInputXItem" 
name="Skippable"/>
+        <bpmn2:inputSet>
+          
<bpmn2:dataInputRefs>_CB75879B-EB0D-4F28-AC61-E6FAA0126490_TaskNameInputX</bpmn2:dataInputRefs>
+          
<bpmn2:dataInputRefs>_CB75879B-EB0D-4F28-AC61-E6FAA0126490_SkippableInputX</bpmn2:dataInputRefs>
+        </bpmn2:inputSet>
+      </bpmn2:ioSpecification>
+      <bpmn2:dataInputAssociation>
+        
<bpmn2:targetRef>_CB75879B-EB0D-4F28-AC61-E6FAA0126490_TaskNameInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from 
xsi:type="bpmn2:tFormalExpression"><![CDATA[Task]]></bpmn2:from>
+          <bpmn2:to 
xsi:type="bpmn2:tFormalExpression"><![CDATA[_CB75879B-EB0D-4F28-AC61-E6FAA0126490_TaskNameInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+      <bpmn2:dataInputAssociation>
+        
<bpmn2:targetRef>_CB75879B-EB0D-4F28-AC61-E6FAA0126490_SkippableInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from 
xsi:type="bpmn2:tFormalExpression"><![CDATA[false]]></bpmn2:from>
+          <bpmn2:to 
xsi:type="bpmn2:tFormalExpression"><![CDATA[_CB75879B-EB0D-4F28-AC61-E6FAA0126490_SkippableInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+      <bpmn2:potentialOwner id="_FbR2ABn5ED6qPL2RHaehww">
+        <bpmn2:resourceAssignmentExpression id="_FbR2ARn5ED6qPL2RHaehww">
+          <bpmn2:formalExpression>jdoe</bpmn2:formalExpression>
+        </bpmn2:resourceAssignmentExpression>
+      </bpmn2:potentialOwner>
+    </bpmn2:userTask>
+    <bpmn2:startEvent id="_F35EC805-22D5-4833-B56D-9DE16B4B258B">
+      <bpmn2:outgoing>_BB186F29-4721-4C71-BFAA-D816C47ED670</bpmn2:outgoing>
+    </bpmn2:startEvent>
+    <bpmn2:boundaryEvent id="_C64FDE83-9AE3-4BC8-94AC-14805BE0D3D3" 
drools:dockerinfo="70.42^74|" drools:boundaryca="true" name="Boundary Timer" 
attachedToRef="_CB75879B-EB0D-4F28-AC61-E6FAA0126490">
+      <bpmn2:extensionElements>
+        <drools:metaData name="elementname">
+          <drools:metaValue><![CDATA[Boundary Timer]]></drools:metaValue>
+        </drools:metaData>
+      </bpmn2:extensionElements>
+      <bpmn2:outgoing>_2CCE6D96-07C9-4196-A3DA-2FA12675606B</bpmn2:outgoing>
+      <bpmn2:timerEventDefinition>
+        <bpmn2:timeDuration 
xsi:type="bpmn2:tFormalExpression">PT180S</bpmn2:timeDuration>
+      </bpmn2:timerEventDefinition>
+    </bpmn2:boundaryEvent>
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram>
+    <bpmndi:BPMNPlane bpmnElement="timers">
+      <bpmndi:BPMNShape id="shape__F35EC805-22D5-4833-B56D-9DE16B4B258B" 
bpmnElement="_F35EC805-22D5-4833-B56D-9DE16B4B258B">
+        <dc:Bounds height="56" width="56" x="325" y="97"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__CB75879B-EB0D-4F28-AC61-E6FAA0126490" 
bpmnElement="_CB75879B-EB0D-4F28-AC61-E6FAA0126490">
+        <dc:Bounds height="102" width="154" x="448" y="74"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__66215F0B-D52C-4DEF-8BAE-3524FD59A246" 
bpmnElement="_66215F0B-D52C-4DEF-8BAE-3524FD59A246">
+        <dc:Bounds height="56" width="56" x="682" y="97"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__C64FDE83-9AE3-4BC8-94AC-14805BE0D3D3" 
bpmnElement="_C64FDE83-9AE3-4BC8-94AC-14805BE0D3D3">
+        <dc:Bounds height="56" width="56" x="518.42" y="148"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__A2B595B9-3809-487E-B354-677DE3EB9B83" 
bpmnElement="_A2B595B9-3809-487E-B354-677DE3EB9B83">
+        <dc:Bounds height="56" width="56" x="682" y="246"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge 
id="edge_shape__F35EC805-22D5-4833-B56D-9DE16B4B258B_to_shape__CB75879B-EB0D-4F28-AC61-E6FAA0126490"
 bpmnElement="_BB186F29-4721-4C71-BFAA-D816C47ED670">
+        <di:waypoint x="353" y="125"/>
+        <di:waypoint x="525" y="125"/>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge 
id="edge_shape__CB75879B-EB0D-4F28-AC61-E6FAA0126490_to_shape__66215F0B-D52C-4DEF-8BAE-3524FD59A246"
 bpmnElement="_217C7B88-D29D-479F-9DDA-49C505B55DB3">
+        <di:waypoint x="525" y="125"/>
+        <di:waypoint x="710" y="125"/>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge 
id="edge_shape__C64FDE83-9AE3-4BC8-94AC-14805BE0D3D3_to_shape__A2B595B9-3809-487E-B354-677DE3EB9B83"
 bpmnElement="_2CCE6D96-07C9-4196-A3DA-2FA12675606B">
+        <di:waypoint x="546.42" y="176"/>
+        <di:waypoint x="546.42" y="274"/>
+        <di:waypoint x="682" y="274"/>
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+  <bpmn2:relationship type="BPSimData">
+    <bpmn2:extensionElements>
+      <bpsim:BPSimData>
+        <bpsim:Scenario id="default" name="Simulationscenario">
+          <bpsim:ScenarioParameters/>
+          <bpsim:ElementParameters 
elementRef="_F35EC805-22D5-4833-B56D-9DE16B4B258B">
+            <bpsim:TimeParameters>
+              <bpsim:ProcessingTime>
+                <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
+              </bpsim:ProcessingTime>
+            </bpsim:TimeParameters>
+          </bpsim:ElementParameters>
+          <bpsim:ElementParameters 
elementRef="_CB75879B-EB0D-4F28-AC61-E6FAA0126490">
+            <bpsim:TimeParameters>
+              <bpsim:ProcessingTime>
+                <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
+              </bpsim:ProcessingTime>
+            </bpsim:TimeParameters>
+            <bpsim:ResourceParameters>
+              <bpsim:Availability>
+                <bpsim:FloatingParameter value="0"/>
+              </bpsim:Availability>
+              <bpsim:Quantity>
+                <bpsim:FloatingParameter value="0"/>
+              </bpsim:Quantity>
+            </bpsim:ResourceParameters>
+            <bpsim:CostParameters>
+              <bpsim:UnitCost>
+                <bpsim:FloatingParameter value="0"/>
+              </bpsim:UnitCost>
+            </bpsim:CostParameters>
+          </bpsim:ElementParameters>
+        </bpsim:Scenario>
+      </bpsim:BPSimData>
+    </bpmn2:extensionElements>
+    <bpmn2:source>_FbQn4Bn5ED6qPL2RHaehww</bpmn2:source>
+    <bpmn2:target>_FbQn4Bn5ED6qPL2RHaehww</bpmn2:target>
+  </bpmn2:relationship>
+</bpmn2:definitions>
\ No newline at end of file
diff --git 
a/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/ManagementAddOnIT.java
 
b/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/ManagementAddOnIT.java
index 95500eb16a..bb38bcca52 100644
--- 
a/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/ManagementAddOnIT.java
+++ 
b/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/ManagementAddOnIT.java
@@ -18,9 +18,12 @@
  */
 package org.kie.kogito.integrationtests.quarkus;
 
+import java.time.ZonedDateTime;
 import java.util.List;
+import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.kie.kogito.process.management.SlaPayload;
 
 import io.quarkus.test.junit.QuarkusIntegrationTest;
 import io.restassured.RestAssured;
@@ -32,14 +35,14 @@ import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.hasItems;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.Matchers.emptyOrNullString;
-import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.*;
 
 @QuarkusIntegrationTest
 class ManagementAddOnIT {
 
     private static final String HELLO1_NODE = 
"_3CDC6E61-DCC5-4831-8BBB-417CFF517CB0";
     private static final String GREETINGS = "greetings";
+    private static final String TIMERS = "timers";
 
     static {
         RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
@@ -151,4 +154,140 @@ class ManagementAddOnIT {
                 .statusCode(200)
                 .extract().response().jsonPath().getList("nodeInstanceId");
     }
+
+    @Test
+    void testManagementTimersEndpoint() {
+        String processInstanceId = given()
+                .body(Map.of())
+                .contentType(ContentType.JSON)
+                .when()
+                .post("/{processId}", TIMERS)
+                .then()
+                .statusCode(201)
+                .body("id", notNullValue())
+                .extract().path("id");
+
+        String nodeInstanceId = given()
+                .when()
+                
.get("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances",
 TIMERS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(1))
+                .extract().path("[0].nodeInstanceId");
+
+        given()
+                .when()
+                
.get("/management/processes/{processId}/instances/{processInstanceId}/timers", 
TIMERS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(4))
+                .body("", hasItem(allOf(
+                        hasEntry("processId", "timers"),
+                        hasEntry("processInstanceId", processInstanceId),
+                        hasKey("timerId"),
+                        hasEntry("description", "[SLA-Process] timers"))))
+                .body("", hasItem(allOf(
+                        hasEntry("processId", "timers"),
+                        hasEntry("processInstanceId", processInstanceId),
+                        hasKey("timerId"),
+                        hasEntry("description", "[CANCEL-Process] timers"))))
+                .body("", hasItem(allOf(
+                        hasEntry("processId", "timers"),
+                        hasEntry("processInstanceId", processInstanceId),
+                        hasEntry("nodeInstanceId", nodeInstanceId),
+                        hasKey("timerId"),
+                        hasEntry("description", "[SLA] Task"))))
+                .body("", hasItem(allOf(
+                        hasEntry("processId", "timers"),
+                        hasEntry("processInstanceId", processInstanceId),
+                        hasEntry("nodeInstanceId", nodeInstanceId),
+                        hasKey("timerId"),
+                        hasEntry("description", "Task-Boundary Timer"))));
+
+        given()
+                .when()
+                
.get("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/timers",
 TIMERS, processInstanceId, nodeInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(2))
+                .body("", hasItem(allOf(
+                        hasEntry("processId", "timers"),
+                        hasEntry("processInstanceId", processInstanceId),
+                        hasEntry("nodeInstanceId", nodeInstanceId),
+                        hasKey("timerId"),
+                        hasEntry("description", "[SLA] Task"))))
+                .body("", hasItem(allOf(
+                        hasEntry("processId", "timers"),
+                        hasEntry("processInstanceId", processInstanceId),
+                        hasEntry("nodeInstanceId", nodeInstanceId),
+                        hasKey("timerId"),
+                        hasEntry("description", "Task-Boundary Timer"))));
+    }
+
+    @Test
+    public void testRescheduleSLATimersEndpoints() {
+        String processInstanceId = given()
+                .contentType(ContentType.JSON)
+                .when()
+                .post("/{processId}", TIMERS)
+                .then()
+                .statusCode(201)
+                .body("id", notNullValue())
+                .extract().path("id");
+
+        String nodeInstanceId = given()
+                .when()
+                
.get("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances",
 TIMERS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(1))
+                .extract().path("[0].nodeInstanceId");
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/sla", 
TIMERS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("message", equalTo("Process Instance '" + 
processInstanceId + "' SLA due date successfully updated"));
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                .when()
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/sla",
 TIMERS, processInstanceId, nodeInstanceId)
+                .then()
+                .statusCode(200)
+                .body("message", equalTo("Node Instance '" + nodeInstanceId + 
"' SLA due date successfully updated"));
+    }
+
+    @Test
+    public void testRescheduleSLATimersProcessInstanceWithoutSLAsConfigured() {
+        String processInstanceId = givenGreetingsProcess();
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/sla", 
GREETINGS, processInstanceId)
+                .then()
+                .statusCode(400)
+                .body(equalTo("Cannot update SLA: Process Instance has NO SLA 
configured"));
+
+        String nodeInstanceId = given()
+                .when()
+                
.get("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances",
 GREETINGS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(2))
+                .extract().path("[0].nodeInstanceId");
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                .when()
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/sla",
 GREETINGS, processInstanceId, nodeInstanceId)
+                .then()
+                .statusCode(400)
+                .body(equalTo("Cannot update SLA: Node has NO SLA 
configured"));
+    }
 }
diff --git 
a/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java
 
b/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java
index b89392c51e..6549825538 100644
--- 
a/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java
+++ 
b/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java
@@ -18,6 +18,9 @@
  */
 package org.kie.kogito.jobs.management.springboot;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.kie.kogito.jobs.JobDescription;
 import org.kie.kogito.jobs.management.RestJobsService;
 import org.kie.kogito.jobs.service.api.Job;
@@ -27,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatusCode;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
@@ -99,6 +103,23 @@ public class SpringRestJobsService extends RestJobsService {
         }
     }
 
+    @Override
+    public String rescheduleJob(JobDescription jobDescription) {
+        String callback = getCallbackEndpoint(jobDescription);
+        LOGGER.debug("Job to be rescheduled {} with callback URL {}", 
jobDescription, callback);
+        final Job job = buildJob(jobDescription, callback);
+        final HttpEntity<String> request = buildJobRequest(job);
+        ResponseEntity<String> response = restTemplate.exchange(
+                getJobsServiceUri(),
+                HttpMethod.PATCH,
+                request,
+                String.class);
+        if 
(response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) {
+            LOGGER.debug("Rescheduling of the job {} done with status code {} 
", job, response.getStatusCode());
+        }
+        return "Job Rescheduled";
+    }
+
     private HttpEntity<String> buildJobRequest(Job job) {
         String json;
         try {
@@ -110,4 +131,18 @@ public class SpringRestJobsService extends RestJobsService 
{
         headers.setContentType(MediaType.APPLICATION_JSON);
         return new HttpEntity<>(json, headers);
     }
+
+    private HttpEntity<String> buildJobRequest(String id) {
+        String json;
+        try {
+            Map<String, String> job = new HashMap<>();
+            job.put("id", id);
+            json = objectMapper.writeValueAsString(job);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException("It was not possible to create the http 
request for the job id: " + id, e);
+        }
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return new HttpEntity<>(json, headers);
+    }
 }
diff --git 
a/springboot/addons/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementRestController.java
 
b/springboot/addons/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementRestController.java
index eec6482af3..55c646b78a 100644
--- 
a/springboot/addons/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementRestController.java
+++ 
b/springboot/addons/process-management/src/main/java/org/kie/kogito/process/management/ProcessInstanceManagementRestController.java
@@ -24,12 +24,7 @@ import 
org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
@@ -152,4 +147,17 @@ public class ProcessInstanceManagementRestController 
extends BaseProcessInstance
         return doCancelProcessInstanceId(processId, processInstanceId);
     }
 
+    @Override
+    @PatchMapping(value = 
"{processId}/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/sla")
+    public ResponseEntity updateNodeInstanceSla(@PathVariable("processId") 
String processId, @PathVariable("processInstanceId") String processInstanceId,
+            @PathVariable("nodeInstanceId") String nodeInstanceId, 
@RequestBody SlaPayload slaPayload) {
+        return doUpdateNodeInstanceSla(processId, processInstanceId, 
nodeInstanceId, slaPayload);
+    }
+
+    @Override
+    @PatchMapping(value = "{processId}/instances/{processInstanceId}/sla")
+    public ResponseEntity updateProcessInstanceSla(@PathVariable("processId") 
String processId, @PathVariable("processInstanceId") String processInstanceId, 
@RequestBody SlaPayload slaPayload) {
+        return doUpdateProcessInstanceSla(processId, processInstanceId, 
slaPayload);
+    }
+
 }
diff --git 
a/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/ManagementAddOnTest.java
 
b/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/ManagementAddOnTest.java
index d15ee2104c..d8e7b94fd1 100644
--- 
a/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/ManagementAddOnTest.java
+++ 
b/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/ManagementAddOnTest.java
@@ -18,12 +18,14 @@
  */
 package org.kie.kogito.integrationtests.springboot;
 
+import java.time.ZonedDateTime;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.kie.kogito.process.management.SlaPayload;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
@@ -38,6 +40,7 @@ import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.CoreMatchers.hasItems;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.Matchers.*;
 
 @ExtendWith(SpringExtension.class)
@@ -200,6 +203,74 @@ class ManagementAddOnTest extends BaseRestTest {
                         hasEntry("description", "Task-Boundary Timer"))));
     }
 
+    @Test
+    public void testRescheduleSLATimersEndpoints() {
+        String processInstanceId = given()
+                .body(Map.of())
+                .contentType(ContentType.JSON)
+                .when()
+                .post("/timers")
+                .then()
+                .statusCode(201)
+                .body("id", notNullValue())
+                .extract().path("id");
+
+        String nodeInstanceId = given()
+                .when()
+                
.get("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances",
 TIMERS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(1))
+                .extract().path("[0].nodeInstanceId");
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/sla", 
TIMERS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("message", equalTo("Process Instance '" + 
processInstanceId + "' SLA due date successfully updated"));
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                .when()
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/sla",
 TIMERS, processInstanceId, nodeInstanceId)
+                .then()
+                .statusCode(200)
+                .body("message", equalTo("Node Instance '" + nodeInstanceId + 
"' SLA due date successfully updated"));
+    }
+
+    @Test
+    public void testRescheduleSLATimersProcessInstanceWithoutSLAsConfigured() {
+        String processInstanceId = givenGreetingsProcess();
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/sla", 
GREETINGS, processInstanceId)
+                .then()
+                .statusCode(400)
+                .body(equalTo("Cannot update SLA: Process Instance has NO SLA 
configured"));
+
+        String nodeInstanceId = given()
+                .when()
+                
.get("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances",
 GREETINGS, processInstanceId)
+                .then()
+                .statusCode(200)
+                .body("$.size()", equalTo(2))
+                .extract().path("[0].nodeInstanceId");
+
+        given()
+                .body(new SlaPayload(ZonedDateTime.now()))
+                .contentType(ContentType.JSON)
+                .when()
+                
.patch("/management/processes/{processId}/instances/{processInstanceId}/nodeInstances/{nodeInstanceId}/sla",
 GREETINGS, processInstanceId, nodeInstanceId)
+                .then()
+                .statusCode(400)
+                .body(equalTo("Cannot update SLA: Node has NO SLA 
configured"));
+    }
+
     private String givenGreetingsProcess() {
         return given().contentType(ContentType.JSON)
                 .when()


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

Reply via email to