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

openmrs / openmrs-core / 27011900216

05 Jun 2026 11:20AM UTC coverage: 63.664% (-0.01%) from 63.677%
27011900216

push

github

web-flow
TRUNK-6558 Return null from getTaskByName when the task does not exist (#6162)

JobRunrSchedulerService.getTaskByName propagated the DAO's
ObjectRetrievalFailureException when no task matched, breaking the
long-standing SchedulerService contract (the pre-JobRunr
TimerSchedulerServiceImpl caught the exception and returned null).
Modules that probe for their task before registering it - e.g.
schedulerService.getTaskByName(NAME) == null - failed to start on any
fresh database.

Restore the historical behavior: catch ObjectRetrievalFailureException,
log a warning, and return null.

0 of 4 new or added lines in 1 file covered. (0.0%)

4 existing lines in 2 files now uncovered.

23827 of 37426 relevant lines covered (63.66%)

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 java.time.Duration;
13
import java.time.Instant;
14
import java.time.ZoneId;
15
import java.time.ZonedDateTime;
16
import java.time.temporal.ChronoUnit;
17
import java.util.Collection;
18
import java.util.Date;
19
import java.util.Iterator;
20
import java.util.List;
21
import java.util.Optional;
22
import java.util.Spliterator;
23
import java.util.Spliterators;
24
import java.util.UUID;
25
import java.util.function.Consumer;
26
import java.util.stream.Collectors;
27
import java.util.stream.Stream;
28
import java.util.stream.StreamSupport;
29

30
import org.jobrunr.jobs.Job;
31
import org.jobrunr.jobs.JobDetails;
32
import org.jobrunr.jobs.JobId;
33
import org.jobrunr.jobs.RecurringJob;
34
import org.jobrunr.jobs.states.StateName;
35
import org.jobrunr.scheduling.JobRequestScheduler;
36
import org.jobrunr.scheduling.JobScheduler;
37
import org.jobrunr.storage.JobNotFoundException;
38
import org.jobrunr.storage.PageRequest;
39
import org.jobrunr.storage.StorageProvider;
40
import org.jspecify.annotations.NonNull;
41
import org.openmrs.User;
42
import org.openmrs.api.APIException;
43
import org.openmrs.api.context.Context;
44
import org.openmrs.api.impl.BaseOpenmrsService;
45
import org.openmrs.scheduler.RecurringTaskDetails;
46
import org.openmrs.scheduler.SchedulerException;
47
import org.openmrs.scheduler.SchedulerService;
48
import org.openmrs.scheduler.SchedulerUtil;
49
import org.openmrs.scheduler.Task;
50
import org.openmrs.scheduler.TaskData;
51
import org.openmrs.scheduler.TaskDefinition;
52
import org.openmrs.scheduler.TaskDetails;
53
import org.openmrs.scheduler.TaskState;
54
import org.openmrs.scheduler.db.SchedulerDAO;
55
import org.openmrs.util.PrivilegeConstants;
56
import org.slf4j.Logger;
57
import org.slf4j.LoggerFactory;
58
import org.springframework.orm.ObjectRetrievalFailureException;
59
import org.springframework.stereotype.Service;
60
import org.springframework.transaction.annotation.Transactional;
61

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

564
        @Override
565
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, Duration interval) {
566
                return scheduleRecurrently(uuid, taskData, interval, taskData.getClass().getSimpleName());
×
567
        }
568
        
569
        @Override
570
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, Duration interval, String name) {
571
                String scheduledBy = getScheduledBySystemId();
1✔
572
                Optional<RecurringJob> existingJob = getRecurringJob(uuid);
1✔
573
                RecurringJob job = existingJob.orElseGet(() -> {
1✔
574
                        String jobId = jobRequestScheduler.scheduleRecurrently(uuid, interval,
1✔
575
                                new JobRequestAdapter(taskData, scheduledBy));
576
                        return updateRecurringJobWithName(jobId, name);
1✔
577
                });
578
                return new JobRunrRecurringTaskDetails(job);
1✔
579
        }
580
        
581
        private String getScheduledBySystemId() {
582
                User user = Context.getAuthenticatedUser();
1✔
583
                return user != null ? user.getSystemId() : "daemon";
1✔
584
        }
585
}        
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