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(){
}
Related
I am running a job scheduler in spring boot application.
To execute job every 5 min, I am using below cron expression
#Scheduled(cron = "0 0/5 * * * *", zone ="EST").
This is working fine
But when I try to execute the same job at a specific time, for instance at 11:30 AM, I am using below cron expression
#Scheduled(cron = "0 30 11 * * *", zone ="EST")
This is not working
I couldn't find any mistake w.r.t syntax. Please help if I am doing anything wrong
We are use spring scheduler with following setting but wondering why its not working for us?
Our expectation is it should execute every day at 2 AM, Is anything wrong with that?
<task:scheduled ref="invoiceScheduler" method="updateInvoiceStatusToOverDue" cron="0 0 2 * * ?" />
Thanks in Advance.
This cron works for my SpringBoot application:
#Scheduled(cron = "0 0 2 1/1 * *")
Btw, what "doesn't work"? Could you please elaborate?
The following task is being scheduled to run 10 minutes past each hour but only during the 8-to-5 "business hours" on weekdays.
scheduler.schedule(task, new CronTrigger("0 10 8-17 * * MON-FRI"));
Could you please try this:
scheduler.schedule(task, new CronTrigger("0 1 2 * * MON-FRI"));
OR
scheduler.schedule(task, new CronTrigger("0 1 2 * * *"));
"0 0 2 * * *" instead of "0 0 2 * * ?" helped us to achieve scheduler to invoke every day at 2 AM. Thanks everyone for finding time and helping me.
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
I tried to use cron expression from this site http://www.cronmaker.com/
#Scheduled(cron = "0 0/15 * 1/1 * ? *")
public void clearRps() {
}
But it throws: java.lang.IllegalStateException: Encountered invalid #Scheduled method 'clearRps': Cron expression must consist of 6 fields (found 7 in "0 0/15 * 1/1 * ? *")
Just use the following cron:
#Scheduled(cron = "0 0/15 * * * *")
Spring cron expression syntax slightly differs from unix cron expression. One immediate difference - it supports 1 less field (6 rather than 7).
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.