I am trying to implement I termediate sla for user tasks which is not provided in rhpam out of the box.
I am using spring boot application to run kie server.
So the ask is whenever there is a user task create those are created with sla due with 8h, I want to get a Kafka message as task status “red” when the 4h are passed. Here is the sample implementation I am planning to do, let me know if this is right approach.
First Custom TaskLifeCycleEventListner
package com.example.listeners;
import org.kie.api.task.TaskEvent;
import org.kie.api.task.TaskLifeCycleEventListener;
import org.kie.api.task.model.Task;
import java.util.concurrent.ConcurrentHashMap;
public class TaskIdEventListener implements TaskLifeCycleEventListener {
private static ConcurrentHashMap<Long, Long> taskSlaMap = new ConcurrentHashMap<>();
@Override
public void afterTaskAddedEvent(TaskEvent event) {
Task task = event.getTask();
long taskId = task.getId();
long slaDueDate = task.getTaskData().getExpirationTime().getTime();
taskSlaMap.put(taskId, slaDueDate);
}
public static ConcurrentHashMap<Long, Long> getTaskSlaMap() {
return taskSlaMap;
}
// Other event methods can be left empty
@Override public void beforeTaskActivatedEvent(TaskEvent event) {}
@Override public void beforeTaskClaimedEvent(TaskEvent event) {}
@Override public void beforeTaskSkippedEvent(TaskEvent event) {}
@Override public void beforeTaskStartedEvent(TaskEvent event) {}
@Override public void beforeTaskStoppedEvent(TaskEvent event) {}
@Override public void beforeTaskCompletedEvent(TaskEvent event) {}
@Override public void beforeTaskFailedEvent(TaskEvent event) {}
@Override public void beforeTaskAddedEvent(TaskEvent event) {}
@Override public void beforeTaskExitedEvent(TaskEvent event) {}
@Override public void afterTaskActivatedEvent(TaskEvent event) {}
@Override public void afterTaskClaimedEvent(TaskEvent event) {}
@Override public void afterTaskSkippedEvent(TaskEvent event) {}
@Override public void afterTaskStartedEvent(TaskEvent event) {}
@Override public void afterTaskStoppedEvent(TaskEvent event) {}
@Override public void afterTaskCompletedEvent(TaskEvent event) {}
@Override public void afterTaskFailedEvent(TaskEvent event) {}
@Override public void afterTaskExitedEvent(TaskEvent event) {}
}
Next Custom SlaChecker class impl
package com.example.schedulers;
import com.example.listeners.TaskIdEventListener;
import org.kie.api.runtime.KieSession;
import org.kie.api.task.TaskService;
import org.kie.api.task.model.Task;
import org.kie.spring.factorybeans.KieSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class SlaChecker {
private final KieSession kieSession;
private final TaskService taskService;
@Autowired
public SlaChecker(KieSession kieSession, TaskService taskService) {
this.kieSession = kieSession;
this.taskService = taskService;
}
public void checkSla() {
ConcurrentHashMap<Long, Long> taskSlaMap = TaskIdEventListener.getTaskSlaMap();
long currentTime = System.currentTimeMillis();
taskSlaMap.forEach((taskId, slaDueDate) -> {
long timeRemaining = slaDueDate - currentTime;
if (timeRemaining <= 4 * 3600 * 1000 && timeRemaining > 3 * 3600 * 1000) {
// 4 hours to 3 hours remaining
Task task = taskService.getTaskById(taskId);
long processInstanceId = task.getTaskData().getProcessInstanceId();
WorkflowProcessInstance processInstance = (WorkflowProcessInstance) kieSession.getProcessInstance(processInstanceId);
if (processInstance != null) {
processInstance.setVariable("slaStatus", "red");
taskService.setOutput(taskId, "slaStatus", "red");
}
}
});
}
}
Next quartz job to check sla
package com.example.schedulers;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class SlaCheckJob implements Job {
private final SlaChecker slaChecker;
public SlaCheckJob(SlaChecker slaChecker) {
this.slaChecker = slaChecker;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
slaChecker.checkSla();
}
}
Next bean registrations
package com.example.config;
import com.example.listeners.TaskIdEventListener;
import com.example.schedulers.SlaCheckJob;
import org.kie.api.runtime.KieSession;
import org.kie.api.task.TaskService;
import org.kie.spring.factorybeans.KieSessionFactoryBean;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
public class ApplicationConfig {
private final KieSession kieSession;
private final TaskService taskService;
@Autowired
public ApplicationConfig(KieSession kieSession, TaskService taskService) {
this.kieSession = kieSession;
this.taskService = taskService;
}
// Quartz Job Configuration
@Bean
public JobDetailFactoryBean slaCheckJobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(SlaCheckJob.class);
factoryBean.setDurability(true);
return factoryBean;
}
@Bean
public SimpleTriggerFactoryBean slaCheckTrigger(JobDetail slaCheckJobDetail) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(slaCheckJobDetail);
factoryBean.setRepeatInterval(60000); // Repeat every minute
factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
return factoryBean;
}
// Quartz Scheduler Configuration
@Bean
public SchedulerFactoryBean schedulerFactoryBean(Trigger slaCheckTrigger) {
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setJobDetails(slaCheckTrigger.getJobDetail());
factoryBean.setTriggers(slaCheckTrigger);
return factoryBean;
}
// Task Event Listener Configuration
@Bean
public TaskIdEventListener taskIdEventListener() {
return new TaskIdEventListener();
}
@Bean
public KieSession kieSession(KieSessionFactoryBean kieSessionFactoryBean, TaskIdEventListener taskIdEventListener) throws Exception {
KieSession kieSession = kieSessionFactoryBean.getObject();
kieSession.getTaskService().addEventListener(taskIdEventListener);
return kieSession;
}
// SlaChecker Configuration (if needed)
// Example: @Bean public SlaChecker slaChecker() { return new SlaChecker(kieSession, taskService); }
}
Let me know if this is overkill and if any easier way exists or this is the right approach?? Thank you.