Testing Quartz CronTrigger trigger - java

Assuming that I have a CronTriggerBean similar to
<bean id="midMonthCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="reminderJobDetail" />
<property name="cronExpression" value="0 0 6 15W * ?" />
</bean>
What is the best way to test that this bean will actually trigger at its specified date, i.e. on the weekday closest to the 15th of each month at 6 AM?
Update: This is supposed to be an unit test, so I'm not going to fire up a VM or change the system time.

Well firstly, there's no point in testing CronTriggerBean itself. It's part of the spring framework, and has already been tested.
A better test might be to test that your cron expression is what you expect. One option here is to use Quartz's CronExpression class. Given a CronExpression object, you can call getNextValidTimeAfter(Date), which returns the next time after the given Date when the expression will fire.

I used CronMaker only to be sure if my cron expression is well formed, check it out:
http://www.cronmaker.com/

You can always wait until the 15h of July.
Being more serious... If it's really a key part of the application and I you need to have it tested fully. I would recommend using some virtualization setups and have the application installed within some guest machine. Then you could play with the system clock and test different date/times without spending a whole month on it. Moreover there's nothing that should stop you from automating such tests.

For those who don't use the Quartz scheduler, but instead use the TaskSchedular directly:
CronSequenceGenerator generator = new CronSequenceGenerator("0 0 8 */1 * *");
Date next = generator.next(prev);

You also can get the trigger bean from spring and invoke the getFireTimeAfter method to finish.

I found a cool documentation here about testing the CronExpression:
http://www.nurkiewicz.com/2012/10/testing-quartz-cron-expressions.html
The C# implementation will be something like this:
void Run()
{
//var collection = findTriggerTimesRecursive(new CronExpression("0 0 17 L-3W 6-9 ? *"), DateTime.UtcNow);
var collection = findTriggerTimesRecursive(new CronExpression("0 0/15 * 1/1 * ? *"), DateTime.UtcNow);
Console.WriteLine(DateTime.UtcNow);
foreach (var item in collection)
{
Console.WriteLine(item);
}
}
public List<DateTimeOffset> findTriggerTimesRecursive(CronExpression expr, DateTimeOffset from, int max = 10)
{
var times = new List<DateTimeOffset>();
var next = expr.GetNextValidTimeAfter(from);
while (next != null && times.Count < max)
{
times.Add(next.Value);
from = next.Value;
next = expr.GetNextValidTimeAfter(from);
}
return times;
}
This is a cool demo. But at the end, I end using Simple Schedule.
var trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithSimpleSchedule(
x =>
{
x.WithIntervalInMinutes(15);
x.RepeatForever();
}
)
.ForJob("myJob", "group1")
.Build();
Because this is executed immediately and then every x time.

Related

Spring SimpleTriggerContext get proper nextExecutionTime

I have job that will run every 10 mins. I don't want to use Spring Scheduler based on last job run next job will schedule to run. Suppose First job ran at 10:15 AM, Subsequent job needs to run at 10:25 AM. When i googled i saw posts with nextExecutionTime. When i use nextExecutionTime my subsequent job is running at 10:20 instead of 10:25. Below is my code, Can any one give an idea how i can run my job at exact 10 mins from last run.
CronTrigger trigger = new CronTrigger("0 0/10 * * * ?");
SimpleTriggerContext triggerContext = new SimpleTriggerContext();
triggerContext.update(Date.from(ZonedDateTime.now().toInstant()), Date.from(ZonedDateTime.now().toInstant()), Date.from(ZonedDateTime.now().toInstant()));
Date nextFireAt = trigger.nextExecutionTime(triggerContext);
System.out.println(nextFireAt);
Can you try the below. I provide below the sequence.
Get the date now.
Add 10 mins to the date and get a new updated date
Update the like triggerContext.update(null, null, date in which you have added 10 mins);

Start a scheduled spring batch job 5 seonds after start of previous one

I have a spring batch job with a #Scheduled(fixedDelay=5000) annotation. But it starts 5 seconds after the end of the previous execution. How can I start it 5 seconds after the start of the previous execution?
Use #Scheduled(fixedRate = 5000). You can also use a CRON expression #Scheduled(cron = "*/5 * * * * ?") but it seems like an overkill.

Expression in a cron

I want to run a cron in java which will run every 'x' hrs daily. X will be decided by a value in the database which is not static. I tried something like this:
private static String cronRunTime = getCronRunTimeFromDb();
private String cronExpression = "* " + cronRunTime + " * * *";
But the main problem I am facing is I am not getting how can I configure this value in the #Scheduled annotation.
I tried something:
#Scheduled(cron = cronExpression)
public void myCron(){
}
But I am getting an error during compilation:
error: element value must be a constant expression
#Scheduled(cron = cronExpression)
Can someone please help me in solving this issue.
"sec min hour day month weekday"
"0 0 * ? * *" is the cron expression for every hour every day.
"0 0 10 ? * *" is the cron expression for 10am every day.
What you're looking for with every "x" hours every day would be:
"0 0 */x ? * *"
Hope that helps.
According to Java specification values of annotation elements for primitive types and String must be constant expressions - something that can be evaluated at compile time, not at runtime.
In short: you cannot pass to an annotation attribute the value that is evaluated at runtime (fetched from database, read from property file, or even returned from a static function like System.currentTimeMillis())
If you have a dynamic logic for your scheduled task that depends on some kind of configuration (in database or property file), you should consider using ScheduledTaskRegistrar and its addCronTask(CronTask task) method, which is of course not as neat as simple annotation.
See example on how to use ScheduledTaskRegistrar among responses here:
Scheduling a job with Spring programmatically (with fixedRate set dynamically)
As an alternative you can use a property placeholder logic to populate cron attribute of #Scheduled annotation as it is described already in other questions:
Task scheduling using cron expression from properties file
Injecting externalized value into Spring annotation
You can even make Spring to read properties from database if you really gonna go wild, see:
Spring PropertyPlaceholderConfigurer load from DB
How to Load Application Properties from Database

How to run a specific set of java code at a particular date and time requested by user

I want my java program to be run at a specific date and time requested by the user which will be in the form of Timestamp the requested timestamp will be stored in the database and the code should start running at that point of time.
should I use Timer class for this or Quartz scheduler. please advice me a better solution. I am new to java so I'm not that familiar with these scheduler. if anyone can help me by giving a simple example it'll be a great help for me how can I give the timestamp as parameter in timer .
for (int i = 0; i < 4; i++)
{
if (bur[i] > 0) {
if (bur[i] > qtm) {
execOrder.add(i + 1);
bur[i] = bur[i] -qtm;
flagClounter++;
} else {
execOrder.add(i + 1);
bur[i] = 0;
flagClounter++;
}
}
}
if the above is the code part ..how can I use it using timer and how to give the Timestamp there or in Quartz. please help me.
Quartz scheduler is a very good option for achieving these kind of functionalities in java..Go with it.. http://www.tutorialsavvy.com/2012/12/quartz-scheduler-scheduling-job-in-java.html
You can use Quartz triggers..
Basically quartz has two type of triggers
1. Simple Trigger
2. Cron Trigger
Suppose if you want to run you Job on a particular Date and Time then use Cron Trigger. Cron trigger accepts Cron Expression which looks like below
expression=59 59 23 FRI * ?
Expression is says job should execute every Friday night at 11:59:59 PM
More expression can be obtained here.
On the other hand Simple trigger accepts milliseconds and executes once your application starts. i.e if Milliseconds specified is 10000, then job executes after 10000 milliseconds once application starts.

How to use #Scheduled(cron) with SpEL in spring?

I have a method that I want spring to schedule - for that matter I'm using the #Scheduled annotation - and to be more exact, I'm using a cron expression.
My cron expression is in a property file that is called scheduler.properties.
When I'm using it as a placeholder #Scheduled(cron="${cron}") - everything works great; but I want to use SpEL ( #Scheduled(cron="#{scheduler['cron']}") ) , and it does't work - throws the following exception:java.lang.IllegalArgumentException: cron expression must consist of 6 fields (found 1 in #{scheduler['cron']})
What am I doing wrong here?
EDIT:
Here is my cron expression from the properties file: cron=0 0/1 * * * ?
Here is the stack trace that I get:
java.lang.IllegalArgumentException: cron expression must consist of 6 fields (found 1 in #{scheduler['cron']})
at org.springframework.scheduling.support.CronSequenceGenerator.parse(CronSequenceGenerator.java:233)
at org.springframework.scheduling.support.CronSequenceGenerator.<init>(CronSequenceGenerator.java:81)
at org.springframework.scheduling.support.CronTrigger.<init>(CronTrigger.java:54)
at org.springframework.scheduling.support.CronTrigger.<init>(CronTrigger.java:44)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.afterPropertiesSet(ScheduledTaskRegistrar.java:188)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:209)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:1)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:97)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:324)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:929)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:467)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4723)
at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5226)
at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5221)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
SECOND EDIT:
It seems that spring is trying to parse the following string as the cron experssion "#{scheduler['cron']}" insraed of the actual cron expression itself.
According to the error message, the value of the cron expression in your properties file is incorrect.
It does not conform to the expected syntax.
The value should contain six fields and look something like this.
* 10 * * * *
Here's the code that throws this exception
/**
* Parse the given pattern expression.
*/
private void parse(String expression) throws IllegalArgumentException {
String[] fields = StringUtils.tokenizeToStringArray(expression, " ");
if (fields.length != 6) {
throw new IllegalArgumentException(String.format(""
+ "cron expression must consist of 6 fields (found %d in %s)", fields.length, expression));
}
It may not be possible to externalize cron configuration using spEL in an Annotation.
The alternatives are to use XML or use the cron expression.
http://forum.springsource.org/showthread.php?91203-Scheduled-and-externalization-of-configuration-for-fixedDelay-and-fixedRate-problem
Always specify like this in property file: Notice the space in between frequency.
run refresh job every day a 9am
job.cron.rate=0 0 9 * * *
Example patterns:
* "0 0 * * * *" = the top of every hour of every day.
* "*/10 * * * * *" = every ten seconds.
* "0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.
* "0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30 and 10 o'clock every day.
* "0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays
* "0 0 0 25 12 ?" = every Christmas Day at midnight
Use it like this in Code:
#Scheduled(cron = "${job.cron.rate}")
public void perform() throws InterruptedException {
}
I had a similar issue and resolved it by reading property file with context:property-placeholder
<util:properties id="applicationProps" location="/WEB-INF/classes/properties/application.properties" />
**<context:property-placeholder properties-ref="applicationProps" />**
Hope it helps someone!!
It works. I spent days figuring out... but this indeed works.
You should set environment variable like you do for JAVA_HOME etc.
Close your IDE.
export cron_scheduler_expression="0 19 21 * * *"
Then restart your IDE, Eclipse or NetBeans whatever you are using.
#Scheduled(cron = "${cron_scheduler_expression}")
public void runSchedulerTask(){
}

Categories

Resources