View Javadoc
1   /*
2    * Copyright (C) 2024 B3Partners B.V.
3    *
4    * SPDX-License-Identifier: MIT
5    */
6   package org.tailormap.api.scheduling;
7   
8   import java.lang.invoke.MethodHandles;
9   import java.time.Instant;
10  import java.time.OffsetDateTime;
11  import java.time.ZoneId;
12  import java.util.Map;
13  import java.util.UUID;
14  import org.quartz.DisallowConcurrentExecution;
15  import org.quartz.InterruptableJob;
16  import org.quartz.JobDataMap;
17  import org.quartz.JobDetail;
18  import org.quartz.JobExecutionContext;
19  import org.quartz.JobExecutionException;
20  import org.quartz.PersistJobDataAfterExecution;
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  import org.springframework.lang.NonNull;
24  import org.springframework.scheduling.quartz.QuartzJobBean;
25  import org.tailormap.api.admin.model.TaskProgressEvent;
26  
27  /** POC task for testing purposes, this is a task that can be interrupted. */
28  @DisallowConcurrentExecution
29  @PersistJobDataAfterExecution
30  public class InterruptablePocTask extends QuartzJobBean implements Task, InterruptableJob {
31  
32    private static final Logger logger =
33        LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
34  
35    private boolean interrupted = false;
36  
37    private String description;
38  
39    @Override
40    public void interrupt() {
41      logger.info("Interrupting POC task");
42      interrupted = true;
43    }
44  
45    @Override
46    protected void executeInternal(@NonNull JobExecutionContext context) throws JobExecutionException {
47  
48      final JobDetail jobDetail = context.getJobDetail();
49  
50      // NOTE: This immutable map is a snapshot of the job data maps at the time of the job execution.
51      final JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
52  
53      // NOTE: This map is mutable and can be used to store job data.
54      final JobDataMap jobDataMap = jobDetail.getJobDataMap();
55  
56      logger.debug(
57          "executing POC task {}:{}, details: {}",
58          jobDetail.getKey().getGroup(),
59          jobDetail.getKey().getName(),
60          mergedJobDataMap.getWrappedMap());
61  
62      TaskProgressEvent progressEvent = new TaskProgressEvent()
63          .startedAt(OffsetDateTime.now(ZoneId.systemDefault()))
64          .type(getType().getValue())
65          .taskData(Map.of("jobKey", jobDetail.getKey().getName()))
66          .uuid(UUID.fromString(jobDetail.getKey().getName()));
67  
68      try {
69        for (int i = 0; i < 110; i += 10) {
70          // Simulate some work for a random period of time
71          long workingTime = (long) (Math.random() * 5000);
72          logger.debug("Working for {} ms", workingTime);
73          Thread.sleep(workingTime);
74          logger.debug("Interruptable POC task is at {}%", i);
75          context.setResult("Interruptable POC task is at %d%%".formatted(i));
76          taskProgress(progressEvent.progress(i).total(100));
77          if (interrupted) {
78            logger.debug("Interruptable POC task interrupted at {}%", Instant.now());
79            jobDataMap.put(
80                Task.LAST_RESULT_KEY,
81                "Interruptable POC task interrupted after %d%% iterations".formatted(i));
82            jobDataMap.put(EXECUTION_FINISHED_KEY, null);
83            context.setResult("Interruptable POC task interrupted after %d%% iterations".formatted(i));
84            // bail out after interruption
85            return;
86          }
87          // after 3rd iteration, interrupt the task
88          if (i == 30) {
89            interrupt();
90          }
91        }
92      } catch (InterruptedException e) {
93        logger.error("Thread interrupted", e);
94      }
95  
96      jobDataMap.put(EXECUTION_COUNT_KEY, (1 + (int) mergedJobDataMap.getOrDefault(EXECUTION_COUNT_KEY, 0)));
97      jobDataMap.put(EXECUTION_FINISHED_KEY, Instant.now());
98      jobDataMap.put(Task.LAST_RESULT_KEY, "Interruptable POC task executed successfully");
99      context.setResult("Interruptable POC task executed successfully");
100   }
101 
102   @Override
103   public TaskType getType() {
104     return TaskType.INTERRUPTABLEPOC;
105   }
106 
107   @Override
108   public String getDescription() {
109     return description;
110   }
111 
112   @Override
113   public void setDescription(String description) {
114     this.description = description;
115   }
116 }