Java task scheduler run daily from start to end date - spring-boot

I've got a spring boot application in java with some scheduled tasks I need to run at midnight every day, from 15th June to 15th August
#Scheduled(cron = "0 0 0 15-30 5 ?") // Midnight every day from 15th June until end of month
public void sendReminderEmailsJune() {
doStuff();
}
#Scheduled(cron = "0 0 0 * 6 ?") // Every day in July
public void sendReminderEmailsJuly() {
doStuff();
}
#Scheduled(cron = "0 0 0 1-15 7 ?") // The first day in August to 15th August
public void sendRemindersEmailsAugust() {
doStuff();
}
Is there a better way to do this so I don't need 3 separate #Scheduled functions?

You could simply repeat these annotations, if you are on
Spring 4 / JDK 8
#Scheduled(cron = "0 0 12 * * ?")
#Scheduled(cron = "0 0 18 * * ?")
public void sendReminderEmails() {...}
else, JDK 6+
#Schedules({
#Scheduled(cron = "0 0 12 * * ?"),
#Scheduled(cron = "0 0 18 * * ?")})
public void sendReminderEmails() {...}

Related

How to sort a list of timings like this [ 2:00 AM , 10:00 AM , 6:00 PM ] in dart-flutter?

I'm working on a flutter app, one of its features is to add your drug dose timings to get a reminder to take your drug. and I need to sort the timings to get the 'next_dose' to appear here :
https://drive.google.com/file/d/1j5KrRbDj0J28_FrMKy7dazk4m9dw202d/view?usp=sharing
this is an example of the list which I want to sort
[8:30 AM, 3:30 PM, 9:30 AM, 7:00 AM]
function I made to get the greater between 2 timings
int greater(String element,String element2){
var hour = element.toString().split(':')[0];
var hour2 = element2.toString().split(':')[0];
var minute = element.toString().split(':')[1].split(' ')[0];
var minute2 = element2.toString().split(':')[1].split(' ')[0];
var day = element.toString().split(':')[1].split(' ')[1];
var day2 = element2.toString().split(':')[1].split(' ')[1];
if(day == 'AM' && day2 == 'PM')
return -1;
else if(day2 == 'AM' && day == 'PM')
return 1;
else if(day == day2)
{
if(int.parse(hour) > int.parse(hour2)) return -1;
else if(int.parse(hour) < int.parse(hour2)) return 1;
else{
if(int.parse(minute) > int.parse(minute2))
return 1;
else
return -1;
}
}
here I tried to use the function to sort the list'Dose'
dose.sort((a,b)=>greater(a,b));
Instead of creating a sort callback with a lot of complicated logic, it'd be simpler if the callback parsed the time strings either into an int or into a DateTime object and compared those. For example:
/// Parses a time of the form 'hh:mm AM' or 'hh:mm PM' to a 24-hour
/// time represented as an int.
///
/// For example, parses '3:30 PM' as 1530.
int parseTime(String time) {
var components = time.split(RegExp('[: ]'));
if (components.length != 3) {
throw FormatException('Time not in the expected format: $time');
}
var hours = int.parse(components[0]);
var minutes = int.parse(components[1]);
var period = components[2].toUpperCase();
if (hours < 1 || hours > 12 || minutes < 0 || minutes > 59) {
throw FormatException('Time not in the expected format: $time');
}
if (hours == 12) {
hours = 0;
}
if (period == 'PM') {
hours += 12;
}
return hours * 100 + minutes;
}
void main() {
var list = ['8:30 AM', '3:30 PM', '9:30 AM', '7:00 AM'];
list.sort((a, b) => parseTime(a).compareTo(parseTime(b)));
print(list); // Prints: [7:00 AM, 8:30 AM, 9:30 AM, 3:30 PM]
}
Alternatively, you can use package:intl and DateFormat.parse to easily parse strings into DateTime objects.
Here we get the time slot by passing the start and end time duration.
List<String> createTimeSlot(
Duration startTime, Duration endTime, BuildContext context,
{Duration step = const Duration(minutes: 30)} // Gap between interval
) {
var timeSlot = <String>[];
var hourStartTime = startTime.inHours;
var minuteStartTime = startTime.inMinutes.remainder(60);
var hourEndTime = endTime.inHours;
var minuteEndTime = endTime.inMinutes.remainder(60);
do {
timeSlot.add(TimeOfDay(hour: hourStartTime, minute: minuteStartTime)
.format(context));
minuteStartTime += step.inMinutes;
while (minuteStartTime >= 60) {
minuteStartTime -= 60;
hourStartTime++;
}
} while (hourStartTime < hourEndTime ||
(hourStartTime == hourEndTime && minuteStartTime <= minuteEndTime));
debugPrint("Number of slot $timeSlot");
return timeSlot;
}
Function call
createTimeSlot(Duration(hours: 1, minutes: 30),
Duration(hours: 3, minutes: 30), context);
Output:
Number of slot [1:30 AM, 2:00 AM, 2:30 AM, 3:00 AM, 3:30 AM]

Do not run scheduled tasks before application is ready

Is it possible to postpone scheduled task (or not execute it at all) until application is ready (i.e. until after ApplicationReadyEvent)?
#Scheduled(cron = "0 0 */4 * * *")
fun foo() {
...
}

sleep using java.time.Duration

I work in java8 and would like to use class java.time.Duration to set up sleep time.
As I understand Duration is composed via seconds and nanoseconds.
So waiting Duration time is should be equivalent to waiting:
java.util.concurrent.TimeUnit.SECONDS.sleep(long);
java.util.concurrent.TimeUnit.NANOSECONDS.sleep(long);
Is my understating is correct?
The TimeUnit.sleep functionality only provides millisecond granularity for sleeping. It forwards to Thread.sleep and the nanoseconds are ignored.
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
If you want something more reliable have a look at LockSupport.parkNanos. You should be able to get something like 50us granularity on your sleep on Linux.
https://hazelcast.com/blog/locksupport-parknanos-under-the-hood-and-the-curious-case-of-parking/

Is there a way to make a #Scheduled function run on initialisation?

I want my program to download from an API every day at 5 AM and at launch. Is there a way to achieve this?
#Scheduled(cron = "0 0 5 * * *", zone = "Europe/Oslo")
fun scheduledDL(link: String) {
val xmlString = downloader.download(link)
}

Spring Quartz Execute Time

I have set an spring quertz timer with following expressions:
#Scheduled(cron = "${quartz.expire.data.cron:0 0 0 * * ?}")
But it starts a little bit ahead as shown in our logs:
2017-10-22 23:59:59.899 scheduler-4
Why?
This might come from the calculation for the next execution time.
In the schedule method of the ReschedulingRunnable, the time is taken from the nextExecutionTime at line 68. The actual delay until execution gets calculated at line 72.
66 public ScheduledFuture<?> schedule() {
67 synchronized (this.triggerContextMonitor) {
68 this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
69 if (this.scheduledExecutionTime == null) {
70 return null;
71 }
72 long initialDelay = this.scheduledExecutionTime.getTime() - System.currentTimeMillis();
73 this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
74 return this;
75 }
76 }
Now let's see what happens in the nextExecutionTime method of the CronTrigger:
72 #Override
73 public Date nextExecutionTime(TriggerContext triggerContext) {
74 Date date = triggerContext.lastCompletionTime();
75 if (date != null) {
76 Date scheduled = triggerContext.lastScheduledExecutionTime();
77 if (scheduled != null && date.before(scheduled)) {
78 // Previous task apparently executed too early...
79 // Let's simply use the last calculated execution time then,
80 // in order to prevent accidental re-fires in the same second.
81 date = scheduled;
82 }
83 }
84 else {
85 date = new Date();
86 }
87 return this.sequenceGenerator.next(date);
88 }
At Line 86, the time is taken. The taken moment is the base to calculate the next execution time for the cron definition which happens in sequenceGenerator.next
134 Calendar calendar = new GregorianCalendar();
135 calendar.setTimeZone(this.timeZone);
136 calendar.setTime(date);
137
138 // First, just reset the milliseconds and try to calculate from there...
139 calendar.set(Calendar.MILLISECOND, 0);
140 long originalTimestamp = calendar.getTimeInMillis();
141 doNext(calendar, calendar.get(Calendar.YEAR));
142
143 if (calendar.getTimeInMillis() == originalTimestamp) {
144 // We arrived at the original timestamp - round up to the next whole second and try again...
145 calendar.add(Calendar.SECOND, 1);
146 doNext(calendar, calendar.get(Calendar.YEAR));
147 }
148
149 return calendar.getTime();
This certainly takes a few milliseconds, which will be missing within the initialDelay.
Proof
A small test to proof this looks like following. I create a regular CronTrigger, and a manipulated CronTrigger
#Test
public void test() {
CronTrigger normalTrigger= new CronTrigger("0 0 0 * * ?");
Date d2 = normalTrigger.nextExecutionTime(new SimpleTriggerContext());
long initialDelay2 = d2.getTime() - System.currentTimeMillis();
System.out.println("Normal trigger:"+ initialDelay2);
//create a manipulated trigger, which takes longer to return the nextExecutionTime
CronTrigger manipulated = new CronTrigger("0 0 0 * * ?") {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date nextExecutionTime = super.nextExecutionTime(triggerContext);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
//ignore
}
return nextExecutionTime;
}
};
Date d = manipulated.nextExecutionTime(new SimpleTriggerContext());
long initialDelay = d.getTime() - System.currentTimeMillis();
System.out.println("Manipulated trigger:" +initialDelay);
}
From the result, you see that the manipulated trigger will trigger 5 seconds earlier than the non-manipulated, because it took 5 seconds longer to return the nextExecutionTime.

Resources