0
# Utilities and Helpers
1
2
Utility classes for date manipulation, cron expression parsing, and other common scheduling operations in Quartz. These helpers simplify complex scheduling tasks and provide convenient methods for working with time, dates, and scheduling patterns.
3
4
## Capabilities
5
6
### Date Utilities
7
8
### DateBuilder Class
9
10
Fluent utility for creating and manipulating Date instances commonly used in scheduling.
11
12
```java { .api }
13
/**
14
* Utility for convenient creation of Date instances for scheduling
15
*/
16
class DateBuilder {
17
/**
18
* Time interval units for date calculations
19
*/
20
enum IntervalUnit {
21
MILLISECOND, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR
22
}
23
24
// Day constants (1-based, Sunday = 1)
25
int SUNDAY = 1;
26
int MONDAY = 2;
27
int TUESDAY = 3;
28
int WEDNESDAY = 4;
29
int THURSDAY = 5;
30
int FRIDAY = 6;
31
int SATURDAY = 7;
32
33
// Month constants (0-based, January = 0)
34
int JANUARY = 0;
35
int FEBRUARY = 1;
36
int MARCH = 2;
37
int APRIL = 3;
38
int MAY = 4;
39
int JUNE = 5;
40
int JULY = 6;
41
int AUGUST = 7;
42
int SEPTEMBER = 8;
43
int OCTOBER = 9;
44
int NOVEMBER = 10;
45
int DECEMBER = 11;
46
47
/**
48
* Create a new DateBuilder instance
49
* @return new builder instance
50
*/
51
static DateBuilder newDate();
52
53
/**
54
* Create a new DateBuilder instance in specified timezone
55
* @param tz the timezone
56
* @return new builder instance
57
*/
58
static DateBuilder newDateInTimezone(TimeZone tz);
59
60
/**
61
* Create a date for today at specified time
62
* @param hour hour of day (0-23)
63
* @param minute minute of hour (0-59)
64
* @param second second of minute (0-59)
65
* @return date for today at specified time
66
*/
67
static Date todayAt(int hour, int minute, int second);
68
69
/**
70
* Create a date for tomorrow at specified time
71
* @param hour hour of day (0-23)
72
* @param minute minute of hour (0-59)
73
* @param second second of minute (0-59)
74
* @return date for tomorrow at specified time
75
*/
76
static Date tomorrowAt(int hour, int minute, int second);
77
78
/**
79
* Create a date in the future by adding interval to current time
80
* @param interval the interval value
81
* @param unit the interval unit
82
* @return future date
83
*/
84
static Date futureDate(int interval, IntervalUnit unit);
85
86
/**
87
* Get the next date that falls on the given minute base
88
* @param date starting date
89
* @param minuteBase minute to align to (0-59)
90
* @return next date aligned to minute base
91
*/
92
static Date nextGivenMinuteDate(Date date, int minuteBase);
93
94
/**
95
* Get next date with seconds set to 0
96
* @param date the date to align
97
* @return date with seconds set to 0
98
*/
99
static Date evenSecondDate(Date date);
100
101
/**
102
* Get next date with minutes and seconds set to 0
103
* @param date the date to align
104
* @return date aligned to hour boundary
105
*/
106
static Date evenMinuteDate(Date date);
107
108
/**
109
* Get next date with hours, minutes, and seconds set to 0
110
* @param date the date to align
111
* @return date aligned to hour boundary
112
*/
113
static Date evenHourDate(Date date);
114
115
/**
116
* Set the year component
117
* @param year the year
118
* @return this builder for method chaining
119
*/
120
DateBuilder atYear(int year);
121
122
/**
123
* Set the month component
124
* @param month the month (0-11, use constants)
125
* @return this builder for method chaining
126
*/
127
DateBuilder atMonth(int month);
128
129
/**
130
* Set the day of month component
131
* @param day the day of month (1-31)
132
* @return this builder for method chaining
133
*/
134
DateBuilder atDay(int day);
135
136
/**
137
* Set the hour component
138
* @param hour the hour (0-23)
139
* @return this builder for method chaining
140
*/
141
DateBuilder atHourOfDay(int hour);
142
143
/**
144
* Set the minute component
145
* @param minute the minute (0-59)
146
* @return this builder for method chaining
147
*/
148
DateBuilder atMinute(int minute);
149
150
/**
151
* Set the second component
152
* @param second the second (0-59)
153
* @return this builder for method chaining
154
*/
155
DateBuilder atSecond(int second);
156
157
/**
158
* Build the Date instance
159
* @return the configured date
160
*/
161
Date build();
162
}
163
```
164
165
**Usage Examples:**
166
167
```java
168
// Quick date creation
169
Date now = new Date();
170
Date in5Minutes = DateBuilder.futureDate(5, DateBuilder.IntervalUnit.MINUTE);
171
Date in2Hours = DateBuilder.futureDate(2, DateBuilder.IntervalUnit.HOUR);
172
Date tomorrow9AM = DateBuilder.tomorrowAt(9, 0, 0);
173
Date today2PM = DateBuilder.todayAt(14, 0, 0);
174
175
// Date alignment
176
Date alignedToMinute = DateBuilder.evenSecondDate(now);
177
Date alignedToHour = DateBuilder.evenMinuteDate(now);
178
Date nextHourMark = DateBuilder.evenHourDate(now);
179
180
// Next occurrence of minute mark (e.g., :15, :30, :45)
181
Date next15MinuteMark = DateBuilder.nextGivenMinuteDate(now, 15);
182
183
// Complex date building
184
Date specificDate = DateBuilder.newDate()
185
.atYear(2024)
186
.atMonth(DateBuilder.DECEMBER)
187
.atDay(25)
188
.atHourOfDay(9)
189
.atMinute(30)
190
.atSecond(0)
191
.build();
192
193
// Timezone-aware date building
194
Date utcDate = DateBuilder.newDateInTimezone(TimeZone.getTimeZone("UTC"))
195
.atYear(2024)
196
.atMonth(DateBuilder.JANUARY)
197
.atDay(1)
198
.atHourOfDay(0)
199
.atMinute(0)
200
.atSecond(0)
201
.build();
202
203
// Using in trigger creation
204
Trigger trigger = newTrigger()
205
.withIdentity("delayedStart")
206
.startAt(DateBuilder.futureDate(30, DateBuilder.IntervalUnit.SECOND))
207
.endAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.HOUR))
208
.withSchedule(simpleSchedule()
209
.withIntervalInMinutes(10)
210
.repeatForever())
211
.build();
212
```
213
214
### TimeOfDay Class
215
216
Represents a specific time of day for use with daily scheduling patterns.
217
218
```java { .api }
219
/**
220
* Represents a time of day (hour, minute, second)
221
*/
222
class TimeOfDay implements Serializable, Comparable<TimeOfDay> {
223
/**
224
* Create time from hour and minute (second defaults to 0)
225
* @param hour hour of day (0-23)
226
* @param minute minute of hour (0-59)
227
* @return TimeOfDay instance
228
*/
229
static TimeOfDay hourAndMinuteOfDay(int hour, int minute);
230
231
/**
232
* Create time from hour, minute, and second
233
* @param hour hour of day (0-23)
234
* @param minute minute of hour (0-59)
235
* @param second second of minute (0-59)
236
* @return TimeOfDay instance
237
*/
238
static TimeOfDay hourMinuteAndSecondOfDay(int hour, int minute, int second);
239
240
/**
241
* Get the hour component
242
* @return hour (0-23)
243
*/
244
int getHour();
245
246
/**
247
* Get the minute component
248
* @return minute (0-59)
249
*/
250
int getMinute();
251
252
/**
253
* Get the second component
254
* @return second (0-59)
255
*/
256
int getSecond();
257
258
/**
259
* Check if this time is before another time
260
* @param timeOfDay the other time
261
* @return true if this time is before the other
262
*/
263
boolean before(TimeOfDay timeOfDay);
264
265
/**
266
* Check if this time is after another time
267
* @param timeOfDay the other time
268
* @return true if this time is after the other
269
*/
270
boolean after(TimeOfDay timeOfDay);
271
272
/**
273
* Get seconds since midnight
274
* @return total seconds since start of day
275
*/
276
int getSecondOfDay();
277
278
/**
279
* Create TimeOfDay from seconds since midnight
280
* @param secondOfDay seconds since start of day
281
* @return TimeOfDay instance
282
*/
283
static TimeOfDay secondOfDay(int secondOfDay);
284
}
285
```
286
287
**Usage Examples:**
288
289
```java
290
// Creating time instances
291
TimeOfDay morning = TimeOfDay.hourAndMinuteOfDay(9, 0); // 9:00 AM
292
TimeOfDay lunch = TimeOfDay.hourAndMinuteOfDay(12, 30); // 12:30 PM
293
TimeOfDay precise = TimeOfDay.hourMinuteAndSecondOfDay(15, 45, 30); // 3:45:30 PM
294
295
// Time comparisons
296
if (morning.before(lunch)) {
297
System.out.println("Morning comes before lunch");
298
}
299
300
// Working with seconds
301
int totalSeconds = lunch.getSecondOfDay(); // Seconds since midnight
302
TimeOfDay reconstructed = TimeOfDay.secondOfDay(totalSeconds);
303
304
// Using in daily time interval schedules
305
Trigger businessHours = newTrigger()
306
.withSchedule(dailyTimeIntervalSchedule()
307
.onMondayThroughFriday()
308
.startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0))
309
.endingDailyAt(TimeOfDay.hourAndMinuteOfDay(17, 0))
310
.withIntervalInMinutes(30))
311
.build();
312
```
313
314
### Cron Expression Utilities
315
316
### CronExpression Class
317
318
Utility for parsing, validating, and evaluating cron expressions.
319
320
```java { .api }
321
/**
322
* Represents and evaluates cron expressions for complex scheduling patterns
323
*/
324
class CronExpression implements Serializable, Cloneable {
325
/**
326
* Create a cron expression from string
327
* @param cronExpression the cron expression string
328
* @throws ParseException if expression is invalid
329
*/
330
CronExpression(String cronExpression) throws ParseException;
331
332
/**
333
* Validate a cron expression string
334
* @param cronExpression the expression to validate
335
* @return true if expression is valid
336
*/
337
static boolean isValidExpression(String cronExpression);
338
339
/**
340
* Get the cron expression string
341
* @return the cron expression
342
*/
343
String getCronExpression();
344
345
/**
346
* Check if the given date satisfies the cron expression
347
* @param date the date to check
348
* @return true if date matches the cron pattern
349
*/
350
boolean isSatisfiedBy(Date date);
351
352
/**
353
* Get the next valid time after the given date
354
* @param date the starting date
355
* @return next date matching the cron expression
356
*/
357
Date getNextValidTimeAfter(Date date);
358
359
/**
360
* Get the next invalid time after the given date
361
* @param date the starting date
362
* @return next date not matching the cron expression
363
*/
364
Date getNextInvalidTimeAfter(Date date);
365
366
/**
367
* Get the time zone for this cron expression
368
* @return the time zone
369
*/
370
TimeZone getTimeZone();
371
372
/**
373
* Set the time zone for this cron expression
374
* @param timeZone the time zone
375
*/
376
void setTimeZone(TimeZone timeZone);
377
378
/**
379
* Get a human-readable summary of the cron expression
380
* @return description of when the expression fires
381
*/
382
String getExpressionSummary();
383
}
384
```
385
386
**Usage Examples:**
387
388
```java
389
try {
390
// Create and validate cron expressions
391
String cronString = "0 0 12 * * ?"; // Daily at noon
392
393
if (CronExpression.isValidExpression(cronString)) {
394
CronExpression cron = new CronExpression(cronString);
395
396
// Set timezone
397
cron.setTimeZone(TimeZone.getTimeZone("America/New_York"));
398
399
// Check if current time matches
400
Date now = new Date();
401
if (cron.isSatisfiedBy(now)) {
402
System.out.println("Current time matches cron expression");
403
}
404
405
// Find next execution times
406
Date nextRun = cron.getNextValidTimeAfter(now);
407
System.out.println("Next execution: " + nextRun);
408
409
Date secondNext = cron.getNextValidTimeAfter(nextRun);
410
System.out.println("Following execution: " + secondNext);
411
412
// Get human-readable description
413
System.out.println("Schedule: " + cron.getExpressionSummary());
414
}
415
416
// Complex cron expressions
417
CronExpression businessHours = new CronExpression("0 */15 9-17 ? * MON-FRI");
418
CronExpression monthlyReport = new CronExpression("0 0 9 1 * ?");
419
CronExpression quarterlyMeeting = new CronExpression("0 0 14 1 1,4,7,10 ?");
420
421
// Calculate execution schedule
422
Date start = new Date();
423
Date end = DateBuilder.futureDate(30, DateBuilder.IntervalUnit.DAY);
424
List<Date> executions = new ArrayList<>();
425
426
Date next = businessHours.getNextValidTimeAfter(start);
427
while (next != null && next.before(end)) {
428
executions.add(next);
429
next = businessHours.getNextValidTimeAfter(next);
430
}
431
432
System.out.println("Found " + executions.size() + " executions in next 30 days");
433
434
} catch (ParseException e) {
435
System.err.println("Invalid cron expression: " + e.getMessage());
436
}
437
438
// Using with cron triggers
439
try {
440
CronExpression cronExpr = new CronExpression("0 30 8 ? * MON-FRI");
441
cronExpr.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
442
443
Trigger cronTrigger = newTrigger()
444
.withSchedule(cronSchedule(cronExpr))
445
.build();
446
447
} catch (ParseException e) {
448
// Handle invalid expression
449
}
450
```
451
452
### TriggerUtils Class
453
454
Utility methods for working with triggers and calculating firing times.
455
456
```java { .api }
457
/**
458
* Utility methods for working with triggers
459
*/
460
class TriggerUtils {
461
/**
462
* Get the next N fire times for a trigger
463
* @param trigger the trigger to analyze
464
* @param cal optional calendar to exclude times
465
* @param numTimes number of fire times to calculate
466
* @return list of next fire times
467
*/
468
static List<Date> computeFireTimes(OperableTrigger trigger, Calendar cal, int numTimes);
469
470
/**
471
* Get fire times between two dates
472
* @param trigger the trigger to analyze
473
* @param cal optional calendar to exclude times
474
* @param from start date
475
* @param to end date
476
* @return list of fire times in range
477
*/
478
static List<Date> computeFireTimesBetween(OperableTrigger trigger, Calendar cal, Date from, Date to);
479
480
/**
481
* Get the final fire time for a trigger
482
* @param trigger the trigger to analyze
483
* @return final fire time or null if infinite
484
*/
485
static Date computeFinalFireTime(OperableTrigger trigger);
486
}
487
```
488
489
**Usage Examples:**
490
491
```java
492
// Analyze trigger firing schedule
493
Trigger trigger = newTrigger()
494
.withSchedule(cronSchedule("0 0 9-17 * * MON-FRI")) // Hourly 9-5, weekdays
495
.startNow()
496
.endAt(DateBuilder.futureDate(30, DateBuilder.IntervalUnit.DAY))
497
.build();
498
499
OperableTrigger operableTrigger = (OperableTrigger) trigger;
500
501
// Get next 10 fire times
502
List<Date> next10 = TriggerUtils.computeFireTimes(operableTrigger, null, 10);
503
System.out.println("Next 10 executions:");
504
for (Date fireTime : next10) {
505
System.out.println(" " + fireTime);
506
}
507
508
// Get fire times for this week
509
Date weekStart = DateBuilder.todayAt(0, 0, 0);
510
Date weekEnd = DateBuilder.futureDate(7, DateBuilder.IntervalUnit.DAY);
511
List<Date> thisWeek = TriggerUtils.computeFireTimesBetween(
512
operableTrigger, null, weekStart, weekEnd);
513
System.out.println("Executions this week: " + thisWeek.size());
514
515
// Check if trigger has finite schedule
516
Date finalFire = TriggerUtils.computeFinalFireTime(operableTrigger);
517
if (finalFire != null) {
518
System.out.println("Final execution: " + finalFire);
519
} else {
520
System.out.println("Trigger runs indefinitely");
521
}
522
```
523
524
### Scheduler Metadata
525
526
### SchedulerMetaData Class
527
528
Provides information about scheduler instance capabilities and status.
529
530
```java { .api }
531
/**
532
* Provides metadata about a scheduler instance
533
*/
534
class SchedulerMetaData {
535
/**
536
* Get the scheduler instance name
537
* @return scheduler name
538
*/
539
String getSchedulerName();
540
541
/**
542
* Get the scheduler instance ID
543
* @return unique instance identifier
544
*/
545
String getSchedulerInstanceId();
546
547
/**
548
* Get the scheduler class being used
549
* @return scheduler implementation class
550
*/
551
Class<?> getSchedulerClass();
552
553
/**
554
* Get the Quartz version
555
* @return version string
556
*/
557
String getVersion();
558
559
/**
560
* Check if scheduler is started
561
* @return true if scheduler is running
562
*/
563
boolean isStarted();
564
565
/**
566
* Check if scheduler is in standby mode
567
* @return true if in standby
568
*/
569
boolean isInStandbyMode();
570
571
/**
572
* Check if scheduler has been shutdown
573
* @return true if shutdown
574
*/
575
boolean isShutdown();
576
577
/**
578
* Get the time when scheduler was started
579
* @return start time or null if not started
580
*/
581
Date getRunningSince();
582
583
/**
584
* Get the number of jobs executed since startup
585
* @return executed job count
586
*/
587
int getNumberOfJobsExecuted();
588
589
/**
590
* Get the job store class being used
591
* @return job store implementation class
592
*/
593
Class<?> getJobStoreClass();
594
595
/**
596
* Check if job store supports persistence
597
* @return true if jobs are persisted
598
*/
599
boolean isJobStoreSupportsPersistence();
600
601
/**
602
* Check if job store is clustered
603
* @return true if clustering is enabled
604
*/
605
boolean isJobStoreClustered();
606
607
/**
608
* Get the thread pool class being used
609
* @return thread pool implementation class
610
*/
611
Class<?> getThreadPoolClass();
612
613
/**
614
* Get the thread pool size
615
* @return number of threads available for job execution
616
*/
617
int getThreadPoolSize();
618
}
619
```
620
621
**Usage Examples:**
622
623
```java
624
// Get scheduler information
625
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
626
SchedulerMetaData metaData = scheduler.getMetaData();
627
628
// Display basic information
629
System.out.println("Scheduler Information:");
630
System.out.println(" Name: " + metaData.getSchedulerName());
631
System.out.println(" Instance ID: " + metaData.getSchedulerInstanceId());
632
System.out.println(" Version: " + metaData.getVersion());
633
System.out.println(" Started: " + metaData.isStarted());
634
635
if (metaData.getRunningSince() != null) {
636
System.out.println(" Running since: " + metaData.getRunningSince());
637
System.out.println(" Jobs executed: " + metaData.getNumberOfJobsExecuted());
638
}
639
640
// Display configuration information
641
System.out.println("\nConfiguration:");
642
System.out.println(" Scheduler class: " + metaData.getSchedulerClass().getSimpleName());
643
System.out.println(" Job store: " + metaData.getJobStoreClass().getSimpleName());
644
System.out.println(" Persistent: " + metaData.isJobStoreSupportsPersistence());
645
System.out.println(" Clustered: " + metaData.isJobStoreClustered());
646
System.out.println(" Thread pool: " + metaData.getThreadPoolClass().getSimpleName());
647
System.out.println(" Thread count: " + metaData.getThreadPoolSize());
648
649
// Health check based on metadata
650
public boolean isSchedulerHealthy(Scheduler scheduler) throws SchedulerException {
651
SchedulerMetaData metaData = scheduler.getMetaData();
652
653
// Check if scheduler is running
654
if (!metaData.isStarted() || metaData.isShutdown()) {
655
return false;
656
}
657
658
// Check if it's been running for reasonable time
659
Date runningSince = metaData.getRunningSince();
660
if (runningSince != null) {
661
long runningTime = System.currentTimeMillis() - runningSince.getTime();
662
if (runningTime < 1000) { // Less than 1 second
663
return false; // Might still be starting up
664
}
665
}
666
667
// Check thread pool size
668
if (metaData.getThreadPoolSize() < 1) {
669
return false;
670
}
671
672
return true;
673
}
674
```
675
676
### Context Classes
677
678
### SchedulerContext Class
679
680
Container for sharing application data across jobs within a scheduler instance.
681
682
```java { .api }
683
/**
684
* Holds application context data for scheduler instance
685
* Extends Map for convenient data access
686
*/
687
class SchedulerContext extends StringKeyDirtyFlagMap {
688
/**
689
* Create empty scheduler context
690
*/
691
SchedulerContext();
692
693
/**
694
* Create scheduler context with initial data
695
* @param map initial context data
696
*/
697
SchedulerContext(Map<?, ?> map);
698
699
// Inherits all Map methods for data manipulation
700
// Plus type-safe accessors similar to JobDataMap
701
}
702
```
703
704
**Usage Examples:**
705
706
```java
707
// Setting up scheduler context
708
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
709
SchedulerContext context = scheduler.getContext();
710
711
// Store application-wide configuration
712
context.put("applicationVersion", "2.1.0");
713
context.put("environment", "production");
714
context.put("databaseUrl", "jdbc:postgresql://db.example.com/app");
715
context.put("maxRetries", 3);
716
context.put("emailService", emailServiceInstance);
717
718
// Jobs can access scheduler context
719
public class ReportJob implements Job {
720
public void execute(JobExecutionContext context) throws JobExecutionException {
721
// Access scheduler context
722
SchedulerContext schedulerContext = context.getScheduler().getContext();
723
724
String version = (String) schedulerContext.get("applicationVersion");
725
String environment = (String) schedulerContext.get("environment");
726
EmailService emailService = (EmailService) schedulerContext.get("emailService");
727
728
// Use shared resources
729
generateReport(version, environment, emailService);
730
}
731
732
private void generateReport(String version, String env, EmailService emailService) {
733
// Implementation using shared context
734
}
735
}
736
737
// Updating context at runtime
738
public void updateConfiguration(Scheduler scheduler, String newDbUrl) throws SchedulerException {
739
SchedulerContext context = scheduler.getContext();
740
context.put("databaseUrl", newDbUrl);
741
742
// All subsequently executed jobs will see the new value
743
}
744
```
745
746
### Key Utility Classes
747
748
### Key Base Class
749
750
Base class for JobKey and TriggerKey providing identity and comparison functionality.
751
752
```java { .api }
753
/**
754
* Base class for keys that identify jobs and triggers
755
*/
756
abstract class Key<T> implements Serializable, Comparable<Key<T>> {
757
String DEFAULT_GROUP = "DEFAULT";
758
759
/**
760
* Get the key name
761
* @return the name component of the key
762
*/
763
String getName();
764
765
/**
766
* Get the key group
767
* @return the group component of the key
768
*/
769
String getGroup();
770
771
/**
772
* Compare keys for ordering
773
* @param other the other key
774
* @return comparison result
775
*/
776
int compareTo(Key<T> other);
777
778
/**
779
* Check equality with another key
780
* @param obj the other object
781
* @return true if keys are equal
782
*/
783
boolean equals(Object obj);
784
785
/**
786
* Get hash code for the key
787
* @return hash code based on name and group
788
*/
789
int hashCode();
790
791
/**
792
* Get string representation
793
* @return formatted string with group and name
794
*/
795
String toString();
796
}
797
```
798
799
**Usage Examples:**
800
801
```java
802
// Key comparison and sorting
803
List<JobKey> jobKeys = Arrays.asList(
804
jobKey("jobA", "group1"),
805
jobKey("jobB", "group1"),
806
jobKey("jobA", "group2")
807
);
808
809
// Sort keys (by group first, then name)
810
Collections.sort(jobKeys);
811
812
// Key equality
813
JobKey key1 = jobKey("myJob", "myGroup");
814
JobKey key2 = jobKey("myJob", "myGroup");
815
System.out.println(key1.equals(key2)); // true
816
817
// Using keys in collections
818
Set<JobKey> uniqueKeys = new HashSet<>();
819
uniqueKeys.add(key1);
820
uniqueKeys.add(key2); // Won't add duplicate
821
822
Map<JobKey, JobDetail> jobMap = new HashMap<>();
823
jobMap.put(key1, jobDetail);
824
825
// Key string representation
826
System.out.println(key1.toString()); // "myGroup.myJob"
827
```
828
829
### Additional Utility Classes
830
831
### TriggerUtils Class
832
833
Utility methods for working with triggers and trigger calculations.
834
835
```java { .api }
836
/**
837
* Utility class for trigger-related operations
838
*/
839
class TriggerUtils {
840
/**
841
* Get the next N fire times for a trigger
842
* @param trigger the trigger to calculate for
843
* @param cal optional calendar for exclusions
844
* @param numTimes number of fire times to calculate
845
* @return list of future fire times
846
*/
847
static List<Date> computeFireTimes(OperableTrigger trigger, Calendar cal, int numTimes);
848
849
/**
850
* Get fire times between two dates
851
* @param trigger the trigger to calculate for
852
* @param cal optional calendar for exclusions
853
* @param from start date
854
* @param to end date
855
* @return list of fire times in the range
856
*/
857
static List<Date> computeFireTimesBetween(OperableTrigger trigger, Calendar cal, Date from, Date to);
858
859
/**
860
* Get the final fire time for a trigger
861
* @param trigger the trigger
862
* @return the final fire time, or null if infinite
863
*/
864
static Date computeEndTimeToAllowParticularNumberOfFirings(OperableTrigger trigger, Calendar cal, int numTimes);
865
866
/**
867
* Make a trigger that will fire at specific times
868
* @param fireTimes list of times when trigger should fire
869
* @return a SimpleTrigger configured for the fire times
870
*/
871
static OperableTrigger makeEvenlySpacedDateTrigger(Date[] fireTimes);
872
}
873
```
874
875
### PropertiesParser Class
876
877
Utility for parsing configuration properties with type conversion.
878
879
```java { .api }
880
/**
881
* Utility for parsing configuration properties
882
*/
883
class PropertiesParser {
884
/**
885
* Create parser for properties
886
* @param props the properties to parse
887
*/
888
PropertiesParser(Properties props);
889
890
/**
891
* Get string property with optional default
892
* @param name property name
893
* @param defaultValue default if not found
894
* @return property value or default
895
*/
896
String getStringProperty(String name, String defaultValue);
897
898
/**
899
* Get required string property
900
* @param name property name
901
* @return property value
902
* @throws SchedulerException if property not found
903
*/
904
String getStringProperty(String name) throws SchedulerException;
905
906
/**
907
* Get boolean property
908
* @param name property name
909
* @param defaultValue default if not found
910
* @return boolean value
911
*/
912
boolean getBooleanProperty(String name, boolean defaultValue);
913
914
/**
915
* Get integer property
916
* @param name property name
917
* @param defaultValue default if not found
918
* @return integer value
919
*/
920
int getIntProperty(String name, int defaultValue);
921
922
/**
923
* Get long property
924
* @param name property name
925
* @param defaultValue default if not found
926
* @return long value
927
*/
928
long getLongProperty(String name, long defaultValue);
929
930
/**
931
* Get float property
932
* @param name property name
933
* @param defaultValue default if not found
934
* @return float value
935
*/
936
float getFloatProperty(String name, float defaultValue);
937
938
/**
939
* Get double property
940
* @param name property name
941
* @param defaultValue default if not found
942
* @return double value
943
*/
944
double getDoubleProperty(String name, double defaultValue);
945
946
/**
947
* Get property names with a given prefix
948
* @param prefix the prefix to match
949
* @return array of matching property names
950
*/
951
String[] getPropertyNames(String prefix);
952
953
/**
954
* Get properties with a given prefix as a new Properties object
955
* @param prefix the prefix to match
956
* @return Properties containing matching entries
957
*/
958
Properties getPropertyGroup(String prefix);
959
960
/**
961
* Get properties with prefix, removing the prefix from keys
962
* @param prefix the prefix to match and remove
963
* @param stripPrefix whether to remove prefix from keys
964
* @return Properties with prefix removed from keys
965
*/
966
Properties getPropertyGroup(String prefix, boolean stripPrefix);
967
}
968
```
969
970
### ClassUtils Class
971
972
Utility methods for class loading and reflection operations.
973
974
```java { .api }
975
/**
976
* Utility class for class loading operations
977
*/
978
class ClassUtils {
979
/**
980
* Load a class using the thread context class loader
981
* @param className fully qualified class name
982
* @return the loaded class
983
* @throws ClassNotFoundException if class not found
984
*/
985
static Class<?> loadClass(String className) throws ClassNotFoundException;
986
987
/**
988
* Load a class using a specific class loader
989
* @param className fully qualified class name
990
* @param classLoader the class loader to use
991
* @return the loaded class
992
* @throws ClassNotFoundException if class not found
993
*/
994
static Class<?> loadClass(String className, ClassLoader classLoader) throws ClassNotFoundException;
995
996
/**
997
* Create an instance of a class
998
* @param className fully qualified class name
999
* @return new instance of the class
1000
* @throws Exception if instantiation fails
1001
*/
1002
static Object instantiateClass(String className) throws Exception;
1003
1004
/**
1005
* Create an instance using a specific class loader
1006
* @param className fully qualified class name
1007
* @param classLoader the class loader to use
1008
* @return new instance of the class
1009
* @throws Exception if instantiation fails
1010
*/
1011
static Object instantiateClass(String className, ClassLoader classLoader) throws Exception;
1012
1013
/**
1014
* Check if a class is available in the classpath
1015
* @param className fully qualified class name
1016
* @return true if class can be loaded
1017
*/
1018
static boolean isClassAvailable(String className);
1019
1020
/**
1021
* Get the thread context class loader
1022
* @return the current thread's context class loader
1023
*/
1024
static ClassLoader getThreadContextClassLoader();
1025
1026
/**
1027
* Set the thread context class loader
1028
* @param classLoader the class loader to set
1029
*/
1030
static void setThreadContextClassLoader(ClassLoader classLoader);
1031
}
1032
```
1033
1034
### Data Structure Utilities
1035
1036
### DirtyFlagMap Class
1037
1038
Map implementation that tracks modifications for change detection.
1039
1040
```java { .api }
1041
/**
1042
* Map that tracks whether its contents have been modified
1043
* @param <K> key type
1044
* @param <V> value type
1045
*/
1046
class DirtyFlagMap<K, V> extends HashMap<K, V> {
1047
/**
1048
* Create empty dirty flag map
1049
*/
1050
DirtyFlagMap();
1051
1052
/**
1053
* Create dirty flag map with initial data
1054
* @param initialCapacity initial capacity
1055
*/
1056
DirtyFlagMap(int initialCapacity);
1057
1058
/**
1059
* Create dirty flag map from existing map
1060
* @param map initial data
1061
*/
1062
DirtyFlagMap(Map<? extends K, ? extends V> map);
1063
1064
/**
1065
* Check if map has been modified since last clearDirtyFlag()
1066
* @return true if map has been modified
1067
*/
1068
boolean isDirty();
1069
1070
/**
1071
* Clear the dirty flag
1072
*/
1073
void clearDirtyFlag();
1074
1075
/**
1076
* Get map of all modified entries since last clearDirtyFlag()
1077
* @return map of modified entries
1078
*/
1079
Map<K, V> getWrappedMap();
1080
1081
// Overrides all modification methods to set dirty flag
1082
}
1083
```
1084
1085
### StringKeyDirtyFlagMap Class
1086
1087
String-keyed version of DirtyFlagMap with type-safe accessors.
1088
1089
```java { .api }
1090
/**
1091
* String-keyed dirty flag map with type-safe accessors
1092
*/
1093
class StringKeyDirtyFlagMap extends DirtyFlagMap<String, Object> {
1094
/**
1095
* Create empty string key dirty flag map
1096
*/
1097
StringKeyDirtyFlagMap();
1098
1099
/**
1100
* Create from existing map
1101
* @param map initial data
1102
*/
1103
StringKeyDirtyFlagMap(Map<?, ?> map);
1104
1105
/**
1106
* Put string value
1107
* @param key the key
1108
* @param value the string value
1109
* @return previous value
1110
*/
1111
Object putAsString(String key, String value);
1112
1113
/**
1114
* Get value as string
1115
* @param key the key
1116
* @return value as string or null
1117
*/
1118
String getString(String key);
1119
1120
/**
1121
* Get boolean value
1122
* @param key the key
1123
* @return boolean value or false if not found
1124
*/
1125
boolean getBoolean(String key);
1126
1127
/**
1128
* Get boolean value with default
1129
* @param key the key
1130
* @param defaultValue default value if not found
1131
* @return boolean value or default
1132
*/
1133
boolean getBooleanValue(String key, boolean defaultValue);
1134
1135
/**
1136
* Get byte value
1137
* @param key the key
1138
* @return byte value or 0 if not found
1139
*/
1140
byte getByte(String key);
1141
1142
/**
1143
* Get character value
1144
* @param key the key
1145
* @return character value or null character if not found
1146
*/
1147
char getChar(String key);
1148
1149
/**
1150
* Get double value
1151
* @param key the key
1152
* @return double value or 0.0 if not found
1153
*/
1154
double getDouble(String key);
1155
1156
/**
1157
* Get float value
1158
* @param key the key
1159
* @return float value or 0.0f if not found
1160
*/
1161
float getFloat(String key);
1162
1163
/**
1164
* Get integer value
1165
* @param key the key
1166
* @return integer value or 0 if not found
1167
*/
1168
int getInt(String key);
1169
1170
/**
1171
* Get integer value with default
1172
* @param key the key
1173
* @param defaultValue default value if not found
1174
* @return integer value or default
1175
*/
1176
int getIntValue(String key, int defaultValue);
1177
1178
/**
1179
* Get long value
1180
* @param key the key
1181
* @return long value or 0L if not found
1182
*/
1183
long getLong(String key);
1184
1185
/**
1186
* Get long value with default
1187
* @param key the key
1188
* @param defaultValue default value if not found
1189
* @return long value or default
1190
*/
1191
long getLongValue(String key, long defaultValue);
1192
1193
/**
1194
* Get short value
1195
* @param key the key
1196
* @return short value or 0 if not found
1197
*/
1198
short getShort(String key);
1199
}
1200
```
1201
1202
**Usage Examples:**
1203
1204
```java
1205
// TriggerUtils usage
1206
SimpleTrigger trigger = (SimpleTrigger) newTrigger()
1207
.withSchedule(simpleSchedule()
1208
.withIntervalInHours(6)
1209
.withRepeatCount(10))
1210
.build();
1211
1212
// Calculate next 5 fire times
1213
List<Date> fireTimes = TriggerUtils.computeFireTimes(trigger, null, 5);
1214
for (Date fireTime : fireTimes) {
1215
System.out.println("Will fire at: " + fireTime);
1216
}
1217
1218
// Calculate fire times in a date range
1219
Date startDate = new Date();
1220
Date endDate = DateBuilder.futureDate(30, DateBuilder.IntervalUnit.DAY);
1221
List<Date> fireTimesInRange = TriggerUtils.computeFireTimesBetween(trigger, null, startDate, endDate);
1222
1223
// PropertiesParser usage
1224
Properties props = new Properties();
1225
props.setProperty("app.name", "MyScheduler");
1226
props.setProperty("app.threadCount", "10");
1227
props.setProperty("app.enabled", "true");
1228
props.setProperty("app.timeout", "30.5");
1229
1230
PropertiesParser parser = new PropertiesParser(props);
1231
1232
String appName = parser.getStringProperty("app.name", "DefaultApp");
1233
int threadCount = parser.getIntProperty("app.threadCount", 5);
1234
boolean enabled = parser.getBooleanProperty("app.enabled", false);
1235
double timeout = parser.getDoubleProperty("app.timeout", 60.0);
1236
1237
// Get all properties with prefix
1238
Properties appProps = parser.getPropertyGroup("app.", true);
1239
// Results in: name=MyScheduler, threadCount=10, enabled=true, timeout=30.5
1240
1241
// ClassUtils usage
1242
try {
1243
// Load class dynamically
1244
Class<?> jobClass = ClassUtils.loadClass("com.example.MyJob");
1245
1246
// Create instance
1247
Job jobInstance = (Job) ClassUtils.instantiateClass("com.example.MyJob");
1248
1249
// Check if class exists
1250
if (ClassUtils.isClassAvailable("com.optional.OptionalFeature")) {
1251
// Use optional feature
1252
}
1253
} catch (Exception e) {
1254
System.err.println("Failed to load class: " + e.getMessage());
1255
}
1256
1257
// DirtyFlagMap usage
1258
DirtyFlagMap<String, String> config = new DirtyFlagMap<>();
1259
config.put("setting1", "value1");
1260
config.put("setting2", "value2");
1261
1262
System.out.println("Is dirty: " + config.isDirty()); // true
1263
1264
config.clearDirtyFlag();
1265
System.out.println("Is dirty: " + config.isDirty()); // false
1266
1267
config.put("setting3", "value3");
1268
System.out.println("Is dirty: " + config.isDirty()); // true
1269
1270
// StringKeyDirtyFlagMap usage (like JobDataMap)
1271
StringKeyDirtyFlagMap data = new StringKeyDirtyFlagMap();
1272
data.put("count", "100");
1273
data.put("enabled", "true");
1274
data.put("rate", "3.14");
1275
1276
// Type-safe accessors
1277
int count = data.getInt("count"); // 100
1278
boolean enabled = data.getBoolean("enabled"); // true
1279
double rate = data.getDouble("rate"); // 3.14
1280
String missing = data.getString("missing"); // null
1281
1282
// With defaults
1283
int defaultCount = data.getIntValue("missingCount", 50); // 50
1284
boolean defaultEnabled = data.getBooleanValue("missingEnabled", false); // false
1285
```