Quartz: 永远不会执行的 Cron 表达式

我知道有一个重复的 给你,这可能正是我的情况,虽然它应该得到一些更好的解释,我将试图在这里提供。

我使用 Spring 应用程序上下文处理 JavaWeb 应用程序。在此上下文中,我使用 Quartz 定义了计划作业。中定义的 cron 触发这些作业。属性档案。

Spring 上下文嵌入在 war 中,而。Properties 文件位于应用程序服务器上(在本例中为 Tomcat)。

这很好,并且允许根据环境(开发、集成、生产、 ...)定义不同的 crons。

现在,当在我自己的计算机上本地运行这个应用程序时,我不希望执行这些作业。有没有一种编写永远不会触发的 cron 表达式的方法?

137823 次浏览

TL;DR

In Quartz 1, you may use this cron: 59 59 23 31 12 ? 2099 (last valid date).
In Quartz 2, you may use this cron: 0 0 0 1 1 ? 2200

Using an expression far in the future

Made some quick tests using org.quartz.CronExpression.

String exp = "0 0 0 1 1 ? 3000";
boolean valid = CronExpression.isValidExpression(exp);
System.out.println(valid);
if (valid) {
CronExpression cronExpression = new CronExpression(exp);
System.out.println(cronExpression.getNextValidTimeAfter(new Date()));
}

When I do String exp = "# 0 0 0 1 1 ?";, the isValid test returns false.

With the sample given above yet, the output is the following:

true
null

Meaning:

  • the expression is valid;
  • there is no upcoming date which matches this expression.

For the scheduler to accept a cron trigger, though, the latter must match a date in the future.

I tried several years and figured out that once the year is above 2300, Quartz seems not to bother anymore (though I did not find a mention to a maximal value for the year in Quartz 2's documentation). There might be a cleaner way to do this, but this will satisfy my needs for now.

So, in the end, the cron I propose is 0 0 0 1 1 ? 2200.

Quartz 1 variant

Note that, in Quartz 1, 2099 is the last valid year. You can therefore adapt your cron expression to use Maciej Matys's suggestion: 59 59 23 31 12 ? 2099

Alternative: Using a date in the past

Arnaud Denoyelle suggested something more elegant, which my test above validates as a correct expression: instead of choosing a date in a far future, choose it in a far past:

0 0 0 1 1 ? 1970 (the first valid expression according to Quartz documentation).

This solution does not work though.

hippofluff highlighted that Quartz will detect an expression in past will never be executed again and therefore throw an exception.

org.quartz.SchedulerException: Based on configured schedule, the given trigger will never fire.

This seems to have been in Quartz for a long time.

Lessons learned: the test is not foolproof as is

This highlights a weakness of my test: in case you want to test a CronExpression, remember it has to have a nextValidTime1. Otherwise, the scheduler you will pass it to will simply reject it with the above mentioned exception.

I would advise adapting the test code as follows:

String exp = "0 0 0 1 1 ? 3000";
boolean valid = CronExpression.isValidExpression(exp);
if (valid) {
CronExpression cronExpression = new CronExpression(exp);
valid = cronExpression.getNextValidTimeAfter(new Date()) != null;
}
System.out.println("Can I use <" + exp + ">? " + (valid ? "Go ahead!" : "This shall fail."));

There you go: no need to think, just read the output.


1 This is the part I forgot when testing Arnaud's solution making me the fool and proving my test wasn't me-proof.

Technically, valid values for the optional Quartz year field are 1970-2099, so 2300 isn't an expected value. I'm assuming you really need to do this and your version of Quartz attempts to enforce valid cron syntax (day 1-31, month 1-12, and so on).

I'm currently using the following code in Resque-scheduler for Rails, which accepts schedule info in validated crontab format, to create a manual-run-only test job:

cron: "0 5 31 2 *"

The job will wait patiently for early morning February 31st before running. For an equivalent in Quartz crontrigger, try this line or some variant thereof:

0 0 5 31 2 ?

Give a try to this one: 59 59 23 31 12 ? 2099

I found this whilst trying to solve a similar problem - disabling a cron expression - but ran into the same problems of requiring a valid future schedule date.

I also hit problems using the 7 value syntax - cannot specify a year in the cron schedule.

So I used this: 0 0 3 ? 2 MON#5

The next times this will execute are:

  1. Monday, February 29, 2044 3:00 AM
  2. Monday, February 29, 2072 3:00 AM
  3. Monday, February 29, 2112 3:00 AM
  4. Monday, February 29, 2140 3:00 AM
  5. Monday, February 29, 2168 3:00 AM

So, essentially, to all intents and purposes, it's disabled. :)

Ah. Curses, this will only work for Quartz scheduler syntax - Spring CronTrigger syntax doesn't allow MON#5 for the fifth monday

So the next best thing is 0 0 3 29 2 ? which will only execute at 3am on the 29th Feb (leap years)

Now, when running this application locally on my own computer, I do not wish these jobs to be executed. Is there a way to write a cron expression which will never trigger?

If you want to disable scheduling on your computer, you have several ways to make that happen.

First you could move the configuration of Quartz to a @Profile-based configuration and not enable this profile locally. Quartz wouldn't start at all if the profile is not active.

An alternative is to configure Quartz to not start automatically. There is a SchedulerFactoryBean#setAutoStartup() that you can set in BeanPostProcessor registered in a dev profile. While this thread is quite old, Spring Boot offers an alternative by registering a SchedulerFactoryBeanCustomizer bean to do the same thing.

If you're using the expression in a @Scheduled(cron="") expression (technically not using quartz, but rather common with spring those days) you can not use the 7-field year-in-the-future solution but those options:

  • If you're using spring 5.1+ (springBoot 2.1+) simply use "${your.cron.prop:-} and don't set the property to disable execution - see @Scheduled. Or set the property itself to "-" (make sure to use quotes if you're using a yml).
  • Disable the bean/service with the @Scheduled method altogether, for example by using a @ConditionalOnProperty("my.scheduleproperty.active") annotation and not setting the property (or setting it to false)

Hi you can try this it will never execute your schedular just pass as - in cron

 @Scheduled(cron = "${schedular.cron.expression}")


schedular.cron.expression=-

Use 31 for any month that has less days than this. Thus, 0 0 31 2 * for February or 0 0 31 5 * for May 0 0 31 6 for June * 0 0 31 9 * for September 0 0 31 11 * for November

should do to prevent the cron from execution. These are valid cron expressions and can be validated on https://crontab.guru/#0_0_31_2_*