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

openmrs / openmrs-core / 26773559161

01 Jun 2026 06:21PM UTC coverage: 63.376% (-0.01%) from 63.389%
26773559161

push

github

web-flow
TRUNK-6429: Create application events for service method calls and entity changes (#6084)

272 of 504 new or added lines in 27 files covered. (53.97%)

5 existing lines in 2 files now uncovered.

23598 of 37235 relevant lines covered (63.38%)

0.63 hits per line

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

9.83
/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.stereotype.Service;
59
import org.springframework.transaction.annotation.Transactional;
60

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

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

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

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

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

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

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

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

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

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

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

240
        @Override
241
        public TaskDefinition getTaskByName(String name) {
242
                return schedulerDAO.getTaskByName(name);
×
243
        }
244

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

260
        @Override
261
        public void saveTaskDefinition(TaskDefinition task) {
262
                if (task.getId() != null) {
×
263
                        schedulerDAO.updateTask(task);
×
264
                } else {
265
                        schedulerDAO.createTask(task);
×
266
                }
267
        }
×
268

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

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

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

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

389
        @Override
390
        public Stream<RecurringTaskDetails> getRecurringTasksByName(String name) {
NEW
391
                return getRecurringTasks().filter(task -> task.getName().equals(name));
×
392
        }
393

394
        @Override
395
        public TaskDetails schedule(TaskData taskData) {
NEW
396
                return schedule(taskData, taskData.getClass().getSimpleName());
×
397
        }
398

399
        @Override
400
        public TaskDetails schedule(TaskData taskData, String name) {
NEW
401
                return schedule(UUID.randomUUID().toString(), taskData, name);
×
402
        }
403

404
        @Override
405
        public TaskDetails schedule(String uuid, TaskData taskData) {
NEW
406
                return schedule(uuid, taskData, taskData.getClass().getSimpleName());
×
407
        }
408

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

448
        @Override
449
        public TaskDetails schedule(TaskData taskData, Instant runAt, String name) {
NEW
450
                return schedule(UUID.randomUUID().toString(), taskData, runAt, name);
×
451
        }
452

453
        @Override
454
        public TaskDetails schedule(String uuid, TaskData taskData, Instant runAt) {
NEW
455
                return schedule(uuid, taskData, runAt, taskData.getClass().getSimpleName());
×
456
        }
457

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

466
        @Override
467
        public TaskDetails schedule(TaskData taskData, ZonedDateTime runAt) {
NEW
468
                return schedule(taskData, runAt, taskData.getClass().getSimpleName());
×
469
        }
470

471
        @Override
472
        public TaskDetails schedule(TaskData taskData, ZonedDateTime runAt, String name) {
NEW
473
                return schedule(UUID.randomUUID().toString(), taskData, runAt, name);
×
474
        }
475

476
        @Override
477
        public TaskDetails schedule(String uuid, TaskData taskData, ZonedDateTime runAt) {
NEW
478
                return schedule(uuid, taskData, runAt, taskData.getClass().getSimpleName());
×
479
        }
480

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

489
        @Override
490
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron) {
NEW
491
                return scheduleRecurrently(taskData, cron, taskData.getClass().getSimpleName());
×
492
        }
493
        
494
        @Override
495
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron, String name) {
NEW
496
                return scheduleRecurrently(UUID.randomUUID().toString(), taskData, cron, name);
×
497
        }
498

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

517
        private @NonNull Optional<RecurringJob> getRecurringJob(String uuid) {
518
                return storageProvider.getRecurringJobs().stream().filter(rj -> rj.getId().equals(uuid)).findAny();
1✔
519
        }
520

521
        @Override
522
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron, ZoneId zoneId) {
NEW
523
                return scheduleRecurrently(taskData, cron, zoneId, taskData.getClass().getSimpleName());
×
524
        }
525

526
        @Override
527
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, String cron, ZoneId zoneId, String name) {
NEW
528
                return scheduleRecurrently(UUID.randomUUID().toString(), taskData, cron, zoneId, name);
×
529
        }
530

531
        @Override
532
        public RecurringTaskDetails scheduleRecurrently(String uuid, TaskData taskData, String cron, ZoneId zoneId) {
NEW
533
                return scheduleRecurrently(uuid, taskData, cron, zoneId, taskData.getClass().getSimpleName());
×
534
        }
535

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

548
        @Override
549
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, Duration interval) {
NEW
550
                return scheduleRecurrently(taskData, interval, taskData.getClass().getSimpleName());
×
551
        }
552
        
553
        @Override
554
        public RecurringTaskDetails scheduleRecurrently(TaskData taskData, Duration interval, String name) {
NEW
555
                return scheduleRecurrently(UUID.randomUUID().toString(), taskData, interval, name);
×
556
        }
557

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