115

I know there is a duplicate here, which probably is exactly my case, though it would deserve some better explanation, which I will try to provide here.

I work with a Java web application using a Spring application context. In this context, I defined scheduled jobs using Quartz. These jobs are triggered by a cron defined in a .properties file.

The Spring context is embedded within the war, while the .properties file is on the application server (Tomcat in this particular case).

This is just fine and allows to define different crons according to the environment (development, integration, production, ...).

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?

Community
  • 1
  • 1
Chop
  • 4,267
  • 5
  • 26
  • 58
  • As in that other question, the only standard way is to begin the command with the `#` comment character. – Barmar Dec 12 '12 at 08:24
  • 1
    I may not have been clear: though the Spring/Quartz framework use the cron syntax, this is not a crontab: the cron is used in a XML field: `` You cannot deactivate the job by commenting the line. Thanks, though. I will use the suggested way specifying a year so far in the future computers as we know them will have disappeared. – Chop Dec 12 '12 at 08:37
  • Apparently Quartz cron expressions are not really like Unix cron expressions, because Unix cron doesn't have seconds or years. The cron tag is probably not appropriate for this question. – Barmar Dec 12 '12 at 08:50
  • You are right, I already met some discrepancies. I will remove the 'cron' tag. – Chop Dec 12 '12 at 09:38

9 Answers9

101

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.

Chop
  • 4,267
  • 5
  • 26
  • 58
  • 3
    Why not `0 0 0 1 1 ? 1970`? Would Quartz refuse it? – Arnaud Denoyelle Jan 15 '15 at 09:45
  • 5
    Quartz 2.1.0 throws a SchedulerException "Based on configured schedule, the given trigger will never file" and startup fails in my Grails app when using Arnaud's suggestion – hippofluff Sep 08 '15 at 15:58
  • @hippofluff Obviously. It was so long when I added it that I had forgotten Quartz will refuse any expression which will have no match in future. Updated the answer, added many mentions to explain. Thank you for the feedback, I'm feeling a bit dumb. – Chop Sep 09 '15 at 05:51
  • 2
    No need to feel that, thanks for updating the answer! +1 :) FYI, the allowed amount of time for a date in the future is 100 years. Anything scheduled to be over 100 years in the future will fail to schedule. Not sure why they settled on 100 years but if an application actually runs long enough to trigger that I would be very impressed lol. Nonetheless if this technology is even known of in 100 years – hippofluff Sep 09 '15 at 13:07
  • 2
    I checked the code[1] for the magic value for year (2200 fails for me) (the javadoc[2] says 2199 is max, the tutorial[3] says 2099 is max). The actual max is: public static final int MAX_YEAR = Calendar.getInstance().get(Calendar.YEAR) + 100; So this year the max is 2116 Next year the max is 2117 [1] https://fisheye.terracotta.org/browse/Quartz/trunk/quartz-core/src/main/java/org/quartz/CronExpression.java?r=2426#to257 [2] http://quartz-scheduler.org/api/2.2.1/org/quartz/CronExpression.html [3] http://quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger – DelGurth Jan 29 '16 at 08:41
  • @DelGurth The max seems to change according to the version of Quartz you are using, which is why I supplied my bit of test code, so that you can test if your regex will be valid or not, and thus choose the cron exp the most adapted to your context. – Chop Jan 29 '16 at 08:49
  • 1
    @Chop Yes thanks for that since it's easy to use. I raised a documentation bug at terracotta since it's currently confusing, even in the actual code they use different max year checks. – DelGurth Jan 29 '16 at 09:05
  • regarding @hippofluff's observation, i have to comment that I can't stand "nanny" software. Sure, throw a WARN, but just DO WHAT I ASK! – ashley Oct 02 '19 at 08:19
  • 2099 is not that far away anymore :) – iTake Mar 10 '21 at 14:54
52

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 ?
Eric Tjossem
  • 2,536
  • 1
  • 15
  • 18
  • 1
    I indeed decreased the year so that Quartz accepted the cron I gave him. Your solution is quite elegant indeed. Thanks! – Chop Dec 20 '12 at 14:12
  • 9
    Hum, finally got the occasion to test this proposal. It appears Quartz refuses this cron as it detects it will never execute... – Chop Jan 08 '13 at 07:03
  • Was worth a shot, since the same method seemed to work fine in Rails. You might look into using `.startAt(startTime)` to have the trigger think it started long ago, then instruct it to only fire on a specific year which has already passed. But this isn't quite as elegant. – Eric Tjossem Jan 08 '13 at 20:17
  • Was worth a shot indeed. I would have loved to use it. I initialize Quartz through Spring XML files, so the `.startAt()` method does not look like an option here. Thanks, though. – Chop Jan 09 '13 at 06:12
  • 1
    The `0 5 31 2 *` one seems to work well in Magento crons. – toon81 Aug 12 '15 at 09:50
  • This may not work in Quartz, but it works perfectly as a standard server cron that you want to postpone execution until some later date when you will come back and activate it again. – Michael Yaeger Dec 23 '16 at 17:10
  • 4
    This doesn't work with an error `Invalid cron expression "0 0 5 31 2 ?" led to runaway search for next trigger` – Frankie Drake Aug 13 '18 at 05:24
  • The next 5 occurrences of the schedule (Cron: '0 5 31 2 *') will be: 12/31/9999 23:59:59 (12/31/9999 23:59:59Z) 12/31/9999 23:59:59 (12/31/9999 23:59:59Z) 12/31/9999 23:59:59 (12/31/9999 23:59:59Z) 12/31/9999 23:59:59 (12/31/9999 23:59:59Z) 12/31/9999 23:59:59 (12/31/9999 23:59:59Z) – Ruslan Mar 14 '22 at 18:30
25

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

metyl
  • 461
  • 4
  • 7
  • Can you explain me what would be the win to use it instead of `0 0 0 1 1 ? 2200`? Mine would be triggered on 2200 Ney Year's Eve, while yours will only be a hundred years before, as I understand it. Am I mistaken? If not, I think the further in future it is, the better, don't you agree? – Chop Feb 08 '13 at 06:42
  • 11
    This the last valid Quartz expression, with Yours Quartz refuses to start job a least Quartz 1.6 and spring 2.5.6SEC3 – metyl Feb 28 '13 at 20:33
  • I looked at the documentation, and you are correct, this is the last valid expression. Still, `0 0 0 1 1 ? 2200` works with the latest version of Quartz. Yet, a smarter solution was proposed (see the edit in my own answer). – Chop Feb 13 '15 at 09:04
19

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)
icyerasor
  • 4,973
  • 1
  • 43
  • 52
19

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

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

schedular.cron.expression=-
Pallav Chanana
  • 617
  • 5
  • 10
  • 1
    This is actually the best answer. Since Spring 5.2 you can use "-" as a special cron expression to disable a trigger. – Rens Verhage Jul 13 '21 at 09:48
8

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)

mrmoosehead
  • 145
  • 1
  • 8
  • This is my favorite workaround ever!! Worths a badge :-) – drizin Aug 13 '19 at 19:19
  • I guess an even better version would be `0 0 29 2 mon#1`, waiting for the next 29th of February being also the first Monday in the month (spoiler alert...) – coconup Sep 16 '22 at 10:15
2

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.

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89
0

We can use the @reboot schedule where it would run once at server startup. I had provided @once as an answer, I realise that it is not an available option, but something we were using as an custom option in my company, for a spark job input. So I am correcting my answer. Reference -> https://ostechnix.com/a-beginners-guide-to-cron-jobs/

Gayathri
  • 95
  • 1
  • 1
  • 11
  • This may be a solution depending on the context. I don't remember the specifics of this one, but there are cases where this could not be applied. For instance, if your scheduled tasks relies on services that are not available from your computer, this wouldn't work. Still, I like this answer very much. It feels less like tinkering and bypassing the tool; it's a real feature instead. Maybe you could just add a link to documentation and excerpt for a more informative answer? – Chop Aug 24 '23 at 08:40
-1

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_*

prem911
  • 266
  • 3
  • 16
  • 1
    As you can see, [this was already proposed](https://stackoverflow.com/a/13938099/1734119) and, though the cron expression is valid, Quartz refuses it. thanks for the idea, though. – Chop Jul 05 '21 at 17:27