• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 28515008125

01 Jul 2026 11:43AM UTC coverage: 63.785% (+0.1%) from 63.651%
28515008125

push

github

rkorytkowski
Fix @since 2.9.x to 2.9.0

24079 of 37750 relevant lines covered (63.79%)

0.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.7
/api/src/main/java/org/openmrs/scheduler/jobrunr/JobRunrSchedulerService.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.scheduler.jobrunr;
11

12
import org.jobrunr.jobs.Job;
13
import org.jobrunr.jobs.JobDetails;
14
import org.jobrunr.jobs.JobId;
15
import org.jobrunr.jobs.RecurringJob;
16
import org.jobrunr.jobs.states.StateName;
17
import org.jobrunr.scheduling.JobRequestScheduler;
18
import org.jobrunr.scheduling.JobScheduler;
19
import org.jobrunr.storage.JobNotFoundException;
20
import org.jobrunr.storage.PageRequest;
21
import org.jobrunr.storage.StorageProvider;
22
import org.jspecify.annotations.NonNull;
23
import org.openmrs.User;
24
import org.openmrs.api.APIException;
25
import org.openmrs.api.context.Context;
26
import org.openmrs.api.impl.BaseOpenmrsService;
27
import org.openmrs.scheduler.RecurringTaskDetails;
28
import org.openmrs.scheduler.SchedulerException;
29
import org.openmrs.scheduler.SchedulerService;
30
import org.openmrs.scheduler.SchedulerUtil;
31
import org.openmrs.scheduler.Task;
32
import org.openmrs.scheduler.TaskData;
33
import org.openmrs.scheduler.TaskDefinition;
34
import org.openmrs.scheduler.TaskDetails;
35
import org.openmrs.scheduler.TaskState;
36
import org.openmrs.scheduler.db.SchedulerDAO;
37
import org.openmrs.util.PrivilegeConstants;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40
import org.springframework.orm.ObjectRetrievalFailureException;
41
import org.springframework.transaction.annotation.Transactional;
42

43
import java.time.Duration;
44
import java.time.Instant;
45
import java.time.ZoneId;
46
import java.time.ZonedDateTime;
47
import java.time.temporal.ChronoUnit;
48
import java.util.Collection;
49
import java.util.Date;
50
import java.util.Iterator;
51
import java.util.List;
52
import java.util.Optional;
53
import java.util.Spliterator;
54
import java.util.Spliterators;
55
import java.util.UUID;
56
import java.util.function.Consumer;
57
import java.util.stream.Collectors;
58
import java.util.stream.Stream;
59
import java.util.stream.StreamSupport;
60

61
/**
62
 * @since 2.9.0
63
 */
64
@Transactional
65
public class JobRunrSchedulerService extends BaseOpenmrsService implements SchedulerService {
66
        
67
        private static final Logger log = LoggerFactory.getLogger(JobRunrSchedulerService.class);
1✔
68
        
69
        private JobRequestScheduler jobRequestScheduler;
70
        
71
        private JobScheduler jobScheduler;
72
        
73
        private StorageProvider storageProvider;
74
        
75
        private SchedulerDAO schedulerDAO;
76
        
77
        public JobRunrSchedulerService(StorageProvider storageProvider, JobRequestScheduler jobRequestScheduler, 
78
                                                                   JobScheduler jobScheduler, SchedulerDAO schedulerDAO) {
1✔
79
                this.jobRequestScheduler = jobRequestScheduler;
1✔
80
                this.jobScheduler = jobScheduler;
1✔
81
                this.storageProvider = storageProvider;
1✔
82
                this.schedulerDAO = schedulerDAO;
1✔
83
        }
1✔
84
        
85
        @Override
86
        public void onStartup() {
87
                for (TaskDefinition taskDefinition: schedulerDAO.getTasks()) {
×
88
                        if (Boolean.TRUE.equals(taskDefinition.getStartOnStartup())) {
×
89
                                String scheduledBy = taskDefinition.getCreator() != null ? taskDefinition.getCreator().getSystemId() : "daemon";
×
90
                                JobId jobId = jobRequestScheduler.enqueue(UUID.fromString(taskDefinition.getUuid()),
×
91
                                        new JobRequestAdapter(taskDefinition, scheduledBy));
92
                                String name = taskDefinition.getName();
×
93
                                if (name == null) {
×
94
                                        name = taskDefinition.getTaskClass();
×
95
                                }
96
                                updateJobWithName(jobId, name);
×
97
                                log.info("Scheduled legacy task {} [{}] to run on startup", name, taskDefinition.getUuid());
×
98
                        }
99
                        
100
                        Optional<RecurringTaskDetails> recurringTask = getRecurringTask(taskDefinition.getUuid());
×
101
                        if (!recurringTask.isPresent()) {
×
102
                                Optional<TaskDetails> task = getTask(taskDefinition.getUuid());
×
103
                                if (!task.isPresent()) {
×
104
                                        try {
105
                                                scheduleTask(taskDefinition);
×
106
                                                log.info("Scheduled legacy task {} [{}] to run on schedule", taskDefinition.getName(), taskDefinition.getUuid());
×
107
                                        } catch (SchedulerException e) {
×
108
                                                throw new APIException(e);
×
109
                                        }
×
110
                                }
111
                        }
112
                }
×
113
        }
×
114

115
        @Override
116
        public String getStatus(Integer id) {
117
                TaskDefinition task = getTask(id);
×
118
                if (task != null && Boolean.TRUE.equals(task.getStarted())) {
×
119
                        return "Scheduled";
×
120
                }
121
                return "Not Running";
×
122
        }
123

124
        @Override
125
        public void shutdownTask(TaskDefinition task) throws SchedulerException {
126
                if (task != null) {
×
127
                        getTask(task.getUuid()).ifPresent(t -> deleteTask(task.getUuid()));
×
128
                        getRecurringTask(task.getUuid()).ifPresent(t -> deleteRecurringTask(task.getUuid()));
×
129
                        task.setStarted(false);
×
130
                        saveTaskDefinition(task);
×
131
                }
132
        }
×
133

134
        @Override
135
        public Task scheduleTask(TaskDefinition task) throws SchedulerException {
136
                if (task != null) {
×
137
                        try {
138
                                String name = task.getName();
×
139
                                if (name == null) {
×
140
                                        name = task.getTaskClass();
×
141
                                }
142
                                String scheduledBy = task.getCreator() != null ? task.getCreator().getSystemId() : "daemon";
×
143
                                
144
                                if (task.getRepeatInterval() != null && task.getRepeatInterval() > 0) {
×
145
                                        if (task.getStartTime() == null) {
×
146
                                                String recurringJobId = jobRequestScheduler.scheduleRecurrently(task.getUuid(),
×
147
                                                        Duration.ofSeconds(task.getRepeatInterval()),
×
148
                                                        new JobRequestAdapter(task, scheduledBy));
149
                                                updateRecurringJobWithName(recurringJobId, name);
×
150
                                        } else {
×
151
                                                Date nextExecution = SchedulerUtil.getNextExecution(task);
×
152
                                                JobId jobId = jobScheduler.schedule(UUID.randomUUID(), nextExecution.toInstant(),
×
153
                                                        (JobRunrSchedulerService service) -> service.scheduleRecurrently(task.getUuid()));
×
154
                                                updateJobWithName(jobId, task.getName());
×
155
                                                // Create a placeholder recurring task that will be updated by the above task to the correct interval
156
                                                String recurringJobId = jobRequestScheduler.scheduleRecurrently(task.getUuid(),
×
157
                                                        Duration.between(Instant.now(), nextExecution.toInstant()).plus(1, ChronoUnit.DAYS),
×
158
                                                        new JobRequestAdapter(task, scheduledBy));
159
                                                updateRecurringJobWithName(recurringJobId, name);
×
160
                                        }
×
161
                                } else if (task.getStartTime() != null){
×
162
                                        Instant runAt = task.getStartTime().toInstant();
×
163
                                        JobId jobId = jobRequestScheduler.schedule(UUID.fromString(task.getUuid()), runAt, 
×
164
                                                new JobRequestAdapter(task, scheduledBy));
165
                                        updateJobWithName(jobId, name);
×
166
                                } else {
×
167
                                        JobId jobId = jobRequestScheduler.enqueue(UUID.fromString(task.getUuid()),
×
168
                                                new JobRequestAdapter(task, scheduledBy));
169
                                        updateJobWithName(jobId, name);
×
170
                                }
171
                                task.setStarted(true);
×
172
                                if (task.getId() != null) {
×
173
                                        schedulerDAO.updateTask(task);
×
174
                                } else {
175
                                        schedulerDAO.createTask(task);
×
176
                                }
177
                        }
178
                        catch (Exception e) {
×
179
                                throw new SchedulerException("Failed to schedule task", e);
×
180
                        }
×
181
                }
182
                return null;
×
183
        }
184

185
        /**
186
         * Helper task to schedule recurrently at a given time.
187
         * @param uuid
188
         */
189
        public void scheduleRecurrently(String uuid) {
190
                TaskDefinition task = getTaskByUuid(uuid);
×
191
                if (task != null) {
×
192
                        String scheduledBy = task.getCreator() != null ? task.getCreator().getSystemId() : "daemon";
×
193
                        String jobId = jobRequestScheduler.scheduleRecurrently(task.getUuid(), Duration.ofSeconds(task.getRepeatInterval()),
×
194
                                new JobRequestAdapter(task, scheduledBy));
195
                        updateRecurringJobWithName(jobId, task.getName());
×
196
                }
197
        }
×
198

199
        @Override
200
        public Task rescheduleTask(TaskDefinition task) throws SchedulerException {
201
                shutdownTask(task);
×
202
                return scheduleTask(task);
×
203
        }
204

205
        @Override
206
        public void rescheduleAllTasks() throws SchedulerException {
207
                for (TaskDefinition task : getScheduledTasks()) {
×
208
                        try {
209
                                rescheduleTask(task);
×
210
                        }
211
                        catch (Exception e) {
×
212
                                log.error("Failed to reschedule task " + task.getName(), e);
×
213
                        }
×
214
                }
×
215
        }
×
216

217
        @Override
218
        public Collection<TaskDefinition> getScheduledTasks() {
219
                return getRegisteredTasks().stream()
×
220
                                .filter(t -> Boolean.TRUE.equals(t.getStarted()))
×
221
                                .collect(Collectors.toList());
×
222
        }
223

224
        @Override
225
        public Collection<TaskDefinition> getRegisteredTasks() {
226
                return schedulerDAO.getTasks();
×
227
        }
228

229
        @Override
230
        public TaskDefinition getTask(Integer id) {
231
                return schedulerDAO.getTask(id);
×
232
        }
233

234
        @Override
235
        public TaskDefinition getTaskByUuid(String uuid) {
236
                return schedulerDAO.getTaskByUuid(uuid);
×
237
        }
238

239
        @Override
240
        public TaskDefinition getTaskByName(String name) {
241
                try {
242
                        return schedulerDAO.getTaskByName(name);
×
243
                } catch (ObjectRetrievalFailureException e) {
×
244
                        log.warn("getTaskByName({}) failed, because: {}", name, e.toString());
×
245
                        return null;
×
246
                }
247
        }
248

249
        @Override
250
        public void deleteTask(Integer id) {
251
                TaskDefinition task = getTask(id);
×
252
                if (task != null) {
×
253
                        if (Boolean.TRUE.equals(task.getStarted())) {
×
254
                                try {
255
                                        shutdownTask(task);
×
256
                                } catch (SchedulerException e) {
×
257
                                        throw new APIException(e);
×
258
                                }
×
259
                        }
260
                        schedulerDAO.deleteTask(id);
×
261
                }
262
        }
×
263

264
        @Override
265
        public void saveTaskDefinition(TaskDefinition task) {
266
                if (task.getId() != null) {
×
267
                        schedulerDAO.updateTask(task);
×
268
                } else {
269
                        schedulerDAO.createTask(task);
×
270
                }
271
        }
×
272

273
        @Override
274
        public void scheduleIfNotRunning(TaskDefinition taskDef) {
275
                if (taskDef != null && !Boolean.TRUE.equals(taskDef.getStarted())) {
×
276
                        try {
277
                                scheduleTask(taskDef);
×
278
                        } catch (SchedulerException e) {
×
279
                                throw new APIException(e);
×
280
                        }
×
281
                }
282
        }
×
283
        
284
        @Override
285
        public Optional<TaskDetails> getTask(String uuid) {
286
                try {
287
                        Job job = storageProvider.getJobById(JobId.parse(uuid));
×
288
                        if (!hasPrivileges(job.getJobDetails())) {
×
289
                                return Optional.empty();
×
290
                        } else {
291
                                return Optional.of(new JobRunrTaskDetails(job));
×
292
                        }
293
                } catch (JobNotFoundException e) {
×
294
                        return Optional.empty();
×
295
                }
296
        }
297

298
        @Override
299
        public Optional<RecurringTaskDetails> getRecurringTask(String uuid) {
300
                return storageProvider.getRecurringJobs().stream()
×
301
                        .filter(r -> r.getId().equals(uuid) && hasPrivileges(r.getJobDetails()))
×
302
                        .findFirst().map(JobRunrRecurringTaskDetails::new);
×
303
        }
304
        
305
        @Override
306
        public void deleteTask(String uuid) {
307
                if (getTask(uuid).isPresent()) {
×
308
                        JobId jobId = JobId.parse(uuid);
×
309
                        jobRequestScheduler.delete(jobId);
×
310
                        storageProvider.deletePermanently(jobId.asUUID());
×
311
                }
312
        }
×
313
        
314
        @Override
315
        public void deleteRecurringTask(String uuid) {
316
                if (getRecurringTask(uuid).isPresent()) {
×
317
                        jobRequestScheduler.delete(uuid);
×
318
                        // Delete any already scheduled task as well
319
                        getTasks(TaskState.SCHEDULED, Instant.now()).forEach(
×
320
                                t -> t.getRecurringTaskUuid().ifPresent(taskUuid -> {
×
321
                                        if (taskUuid.equals(uuid)) {
×
322
                                                deleteTask(t.getUuid());
×
323
                                        }
324
                                })
×
325
                        );
326
                }
327
        }
×
328
        
329
        @Override
330
        public Stream<TaskDetails> getTasks(TaskState state, Instant before) {
331
                return StreamSupport.stream(new Spliterators.AbstractSpliterator<TaskDetails>(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.NONNULL) {
×
332
                        private int offset = 0;
×
333
                        private Iterator<Job> currentBatch;
334
                        private final boolean isSchedulerManager = Context.hasPrivilege(PrivilegeConstants.MANAGE_SCHEDULER);
×
335
                        private final User user = Context.getAuthenticatedUser();
×
336
                        private final String userSystemId = user != null ? user.getSystemId() : null;
×
337

338
                        @Override
339
                        public boolean tryAdvance(Consumer<? super TaskDetails> action) {
340
                                if (currentBatch == null || !currentBatch.hasNext()) {
×
341
                                        int limit = 100;
×
342
                                        List<Job> jobs = storageProvider.getJobs(StateName.valueOf(state.name()), before, PageRequest.ascOnUpdatedAt(offset, limit));
×
343
                                        if (jobs == null || jobs.isEmpty()) {
×
344
                                                return false;
×
345
                                        }
346
                                        if (!isSchedulerManager) {
×
347
                                                // List only tasks for the currently authenticated user
348
                                                currentBatch = jobs.stream().filter(job -> userSystemId != null && isScheduledBy(job.getJobDetails(), userSystemId)).iterator();
×
349
                                        } else {
350
                                                currentBatch = jobs.iterator();
×
351
                                        }
352
                                        offset += jobs.size();
×
353
                                }
354
                                
355
                                if (currentBatch.hasNext()) {
×
356
                                        action.accept(new JobRunrTaskDetails(currentBatch.next()));
×
357
                                        return true;
×
358
                                }
359
                                return false;
×
360
                        }
361
                }, false);
362
        }
363
        
364
        public boolean hasPrivileges(JobDetails jobDetails) {
365
                if (Context.hasPrivilege(PrivilegeConstants.MANAGE_SCHEDULER)) {
×
366
                        return true;
×
367
                }
368
                User user = Context.getAuthenticatedUser();
×
369
                return user != null && isScheduledBy(jobDetails, user.getSystemId());
×
370
        }
371

372
        public boolean isScheduledBy(JobDetails jobDetails, String userSystemId) {
373
                return jobDetails.getJobParameters().stream()
×
374
                        .anyMatch(jp -> {
×
375
                                if (jp.getObject() instanceof JobRequestAdapter) {
×
376
                                        JobRequestAdapter jobRequestAdapter = (JobRequestAdapter) jp.getObject();
×
377
                                        return jobRequestAdapter.getUserSystemId().equals(userSystemId);
×
378
                                }
379
                                return false;
×
380
                        });
381
        }
382
        
383
        @Override
384
        public Stream<RecurringTaskDetails> getRecurringTasks() {
385
                boolean isSchedulerManager = Context.hasPrivilege(PrivilegeConstants.MANAGE_SCHEDULER);
×
386
                User user = Context.getAuthenticatedUser();
×
387
                String userSystemId = user != null ? user.getSystemId() : null;
×
388
                return storageProvider.getRecurringJobs().stream().filter(
×
389
                        j -> isSchedulerManager || (userSystemId != null && isScheduledBy(j.getJobDetails(), userSystemId)))
×
390
                        .map(JobRunrRecurringTaskDetails::new);
×
391
        }
392

393
        @Override
394
        public Stream<RecurringTaskDetails> getRecurringTasksByName(String name) {
395
                return getRecurringTasks().filter(task -> task.getName().equals(name));
×
396
        }
397

398
        @Override
399
        public TaskDetails schedule(TaskData taskData) {
400
                return schedule(taskData, taskData.getClass().getSimpleName());
×
401
        }
402

403
        @Override
404
        public TaskDetails schedule(TaskData taskData, String name) {
405
                return schedule(UUID.randomUUID().toString(), taskData, name);
×
406
        }
407

408
        @Override
409
        public TaskDetails schedule(String uuid, TaskData taskData) {
410
                return schedule(uuid, taskData, taskData.getClass().getSimpleName());
×
411
        }
412

413
        @Override
414
        public TaskDetails schedule(String uuid, TaskData taskData, String name) {
415
                String scheduledBy = getScheduledBySystemId();
×
416
                JobId jobId = jobRequestScheduler.enqueue(UUID.fromString(uuid), new JobRequestAdapter(taskData, scheduledBy));
×
417
                Job job = updateJobWithName(jobId, name);
×
418
                return new JobRunrTaskDetails(job);
×
419
        }
420
        
421
        private Job updateJobWithName(JobId jobId, String name) {
422
                try {
423
                        Job job = storageProvider.getJobById(jobId);
×
424
                        
425
                        if (name != null) {
×
426
                                job.setJobName(name);
×
427
                                job = storageProvider.save(job);
×
428
                        }
429
                        return job;
×
430
                } catch (JobNotFoundException e) {
×
431
                        throw new IllegalStateException(e);
×
432
                }
433
        }
434
        
435
        private RecurringJob updateRecurringJobWithName(String uuid, String name) {
436
                RecurringJob recurringJob = storageProvider.getRecurringJobs().stream()
1✔
437
                        .filter(rj -> rj.getId().equals(uuid)).findFirst()
1✔
438
                        .orElseThrow(() -> new APIException("Job " + uuid + " not found"));
1✔
439
                
440
                if (name != null) {
1✔
441
                        recurringJob.setJobName(name);
1✔
442
                        storageProvider.saveRecurringJob(recurringJob);
1✔
443
                }
444
                return recurringJob;
1✔
445
        }
446
        
447
        @Override
448
        public TaskDetails schedule(TaskData taskData, Instant runAt) {
449
                return schedule(taskData, runAt, taskData.getClass().getSimpleName());
×
450
        }
451

452
        @Override
453
        public TaskDetails schedule(TaskData taskData, Instant runAt, String name) {
454
                return schedule(UUID.randomUUID().toString(), taskData, runAt, name);
×
455
        }
456

457
        @Override
458
        public TaskDetails schedule(String uuid, TaskData taskData, Instant runAt) {
459
                return schedule(uuid, taskData, runAt, taskData.getClass().getSimpleName());
×
460
        }
461

462
        @Override
463
        public TaskDetails schedule(String uuid, TaskData taskData, Instant runAt, String name) {
464
                String scheduledBy = getScheduledBySystemId();
×
465
                JobId jobId = jobRequestScheduler.schedule(UUID.fromString(uuid), runAt, new JobRequestAdapter(taskData, scheduledBy));
×
466
                Job job = updateJobWithName(jobId, name);
×
467
                return new JobRunrTaskDetails(job);
×
468
        }
469

470
        @Override
471
        public TaskDetails schedule(TaskData taskData, ZonedDateTime runAt) {
472
                return schedule(taskData, runAt, taskData.getClass().getSimpleName());
×
473
        }
474

475
        @Override
476
        public TaskDetails schedule(TaskData taskData, ZonedDateTime runAt, String name) {
477
                return schedule(UUID.randomUUID().toString(), taskData, runAt, name);
×
478
        }
479

480
        @Override
481
        public TaskDetails schedule(String uuid, TaskData taskData, ZonedDateTime runAt) {
482
                return schedule(uuid, taskData, runAt, taskData.getClass().getSimpleName());
×
483
        }
484

485
        @Override
486
        public TaskDetails schedule(String uuid, TaskData taskData, ZonedDateTime runAt, String name) {
487
                String scheduledBy = getScheduledBySystemId();
×
488
                JobId jobId = jobRequestScheduler.schedule(UUID.fromString(uuid), runAt, new JobRequestAdapter(taskData, scheduledBy));
×
489
                Job job = updateJobWithName(jobId, name);
×
490
                return new JobRunrTaskDetails(job);
×
491
        }
492

493
        @Override
494
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron) {
495
                return scheduleRecurrently(taskData, cron, taskData.getClass().getSimpleName());
×
496
        }
497
        
498
        @Override
499
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron, String name) {
500
                return scheduleRecurrently(UUID.randomUUID().toString(), taskData, cron, name);
×
501
        }
502

503
        @Override
504
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, String cron) {
505
                return scheduleRecurrently(uuid, taskData, cron, taskData.getClass().getSimpleName());
×
506
        }
507
        
508
        @Override
509
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, String cron, String name) {
510
                String scheduledBy = getScheduledBySystemId();
×
511
                Optional<RecurringJob> existingJob = getRecurringJob(uuid);
×
512
                RecurringJob job = existingJob.orElseGet(() -> {
×
513
                        String jobId = jobRequestScheduler.scheduleRecurrently(uuid, cron,
×
514
                                new JobRequestAdapter(taskData, scheduledBy));
515
                        return updateRecurringJobWithName(jobId, name);
×
516
                });
517
                
518
                return new JobRunrRecurringTaskDetails(job);
×
519
        }
520

521
        private @NonNull Optional<RecurringJob> getRecurringJob(String uuid) {
522
                return storageProvider.getRecurringJobs().stream().filter(rj -> rj.getId().equals(uuid)).findAny();
1✔
523
        }
524

525
        @Override
526
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron, ZoneId zoneId) {
527
                return scheduleRecurrently(taskData, cron, zoneId, taskData.getClass().getSimpleName());
×
528
        }
529

530
        @Override
531
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron, ZoneId zoneId, String name) {
532
                return scheduleRecurrently(UUID.randomUUID().toString(), taskData, cron, zoneId, name);
×
533
        }
534

535
        @Override
536
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, String cron, ZoneId zoneId) {
537
                return scheduleRecurrently(uuid, taskData, cron, zoneId, taskData.getClass().getSimpleName());
×
538
        }
539

540
        @Override
541
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, String cron, ZoneId zoneId, String name) {
542
                String scheduledBy = getScheduledBySystemId();
×
543
                Optional<RecurringJob> existingJob = getRecurringJob(uuid);
×
544
                RecurringJob job = existingJob.orElseGet(() -> {
×
545
                        String jobId = jobRequestScheduler.scheduleRecurrently(uuid, cron, zoneId,
×
546
                                new JobRequestAdapter(taskData, scheduledBy));
547
                        return updateRecurringJobWithName(jobId, name);
×
548
                });
549
                return new JobRunrRecurringTaskDetails(job);
×
550
        }
551

552
        @Override
553
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, Duration interval) {
554
                return scheduleRecurrently(taskData, interval, taskData.getClass().getSimpleName());
×
555
        }
556
        
557
        @Override
558
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, Duration interval, String name) {
559
                return scheduleRecurrently(UUID.randomUUID().toString(), taskData, interval, name);
×
560
        }
561

562
        @Override
563
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, Duration interval) {
564
                return scheduleRecurrently(uuid, taskData, interval, taskData.getClass().getSimpleName());
×
565
        }
566
        
567
        @Override
568
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, Duration interval, String name) {
569
                String scheduledBy = getScheduledBySystemId();
1✔
570
                Optional<RecurringJob> existingJob = getRecurringJob(uuid);
1✔
571
                RecurringJob job = existingJob.orElseGet(() -> {
1✔
572
                        String jobId = jobRequestScheduler.scheduleRecurrently(uuid, interval,
1✔
573
                                new JobRequestAdapter(taskData, scheduledBy));
574
                        return updateRecurringJobWithName(jobId, name);
1✔
575
                });
576
                return new JobRunrRecurringTaskDetails(job);
1✔
577
        }
578
        
579
        private String getScheduledBySystemId() {
580
                User user = Context.getAuthenticatedUser();
1✔
581
                return user != null ? user.getSystemId() : "daemon";
1✔
582
        }
583
}        
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc