web-dev-qa-db-fra.com

Quartz: expression Cron qui ne s'exécutera jamais

Je sais qu'il y a un doublon ici , ce qui est probablement mon cas, bien que cela mériterait une meilleure explication, que je vais essayer de fournir ici.

Je travaille avec une application Web Java) à l'aide d'un contexte d'application Spring. Dans ce contexte, j'ai défini des travaux planifiés à l'aide de Quartz. Ces travaux sont déclenchés par un cron défini dans un fichier .properties.

Le contexte Spring est intégré à la guerre, tandis que le fichier .properties se trouve sur le serveur d'applications (Tomcat dans ce cas particulier).

C'est très bien et permet de définir différents crons en fonction de l'environnement (développement, intégration, production, ...).

Maintenant, lors de l'exécution de cette application localement sur mon propre ordinateur, je ne souhaite pas que ces travaux soient exécutés. Y a-t-il un moyen d'écrire une expression cron qui ne se déclenchera jamais?

71
Chop

TL; DR

Dans Quartz 1, vous pouvez utiliser ce cron: 59 59 23 31 12 ? 2099 _ (dernière date valide).
Dans le Quartz 2, vous pouvez utiliser ce code: 0 0 0 1 1 ? 2200

Utiliser une expression loin dans le futur

Fait quelques tests rapides en utilisant 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()));
}

Quand je fais String exp = "# 0 0 0 1 1 ?";, le test isValid renvoie false.

Avec l'exemple donné ci-dessus, le résultat est le suivant:

true
null

Sens:

  • l'expression est valide;
  • il n'y a pas de date à venir qui correspond à cette expression.

Pour que le planificateur accepte un déclencheur cron, ce dernier doit correspond à une date ultérieure.

J'ai essayé plusieurs années et j'ai compris qu'une fois que l'année était au-dessus de 2300, Quartz ne semble plus être dérangé (bien que je n'ai pas trouvé de mention d'une valeur maximale pour l'année dans la documentation de Quartz 2 ). Il y a peut-être une façon plus propre de faire cela, mais cela satisfera mes besoins pour le moment.

Donc, finalement, le cron que je propose est 0 0 0 1 1 ? 2200.

Quartz 1 variante

Notez que, dans le Quartz 1, 2099 est la dernière année valide . Vous pouvez donc adapter votre expression cron à utiliser suggestion de Maciej Matys : 59 59 23 31 12 ? 2099

Alternative: utiliser une date dans le passé

Arnaud Denoyelle a suggéré quelque chose de plus élégant, que mon test ci-dessus valide comme une expression correcte: au lieu de choisir une date dans un futur lointain, choisissez-la dans un passé lointain:

0 0 0 1 1 ? 1970 (la première expression valide selon la documentation Quartz).

Cette solution ne fonctionne pas cependant.

hippofluff a souligné que Quartz détectera une expression dans le passé ne sera jamais exécuté à nouveau et lance donc une exception.

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

Cela semble avoir été en Quartz pendant une longue période .

Leçons apprises: le test n'est pas infaillible tel quel

Cela met en évidence une faiblesse de mon test: si vous voulez tester un CronExpression, rappelez-vous qu'il doit avoir un nextValidTime1. Sinon, le planificateur auquel vous le transmettez le rejettera simplement avec l'exception susmentionnée.

Je conseillerais d’adapter le code de test comme suit:

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."));

Voilà: pas besoin de réfléchir, il suffit de lire le résultat.


1 C'est la partie que j'ai oubliée en testant la solution d'Arnaud, ce qui fait de moi un imbécile et prouvant que mon test n'était pas à l'abri de toute épreuve.

62
Chop

Techniquement, les valeurs valides pour le champ facultatif de l'année Quartz sont 1970-2099, donc 2300 n'est pas une valeur attendue. Je suppose que vous devez vraiment faire ceci et que votre version de Quartz tente d'appliquer une syntaxe cron valide (jour 1-31, mois 1-12, etc).

J'utilise actuellement le code suivant dans Resque-scheduler for Rails, qui accepte les informations de planification dans un format de fichier crontab validé, pour créer un travail de test à exécution manuelle uniquement:

cron: "0 5 31 2 *"

Le travail attendra patiemment tôt le matin le 31 février avant de courir. Pour un équivalent en Quartz crontrigger , essayez cette ligne ou une variante de celle-ci:

0 0 5 31 2 ?
36
Eric Tjossem

Essayez celui-ci: 59 59 23 31 12 ? 2099

25
Maciej Matys

J'ai trouvé cela en essayant de résoudre un problème similaire - désactiver une expression cron - mais je rencontrais le même problème pour demander une date de planification future valide.

Je rencontre aussi des problèmes en utilisant la syntaxe à 7 valeurs - je ne peux pas spécifier une année dans le programme cron.

J'ai donc utilisé ceci: 0 0 3? 2 LUN N ° 5

Les prochaines fois que cela s'exécutera sont:

  1. Lundi 29 février 2044 03h00
  2. Lundi 29 février 2072 à 03h00
  3. Lundi 29 février 21h12 à 03h00
  4. Lundi 29 février 21h40
  5. Lundi 29 février 2168 à 3h00

Donc, à toutes fins pratiques, il est désactivé. :)

Ah Curses, cela ne fonctionnera que pour la syntaxe du planificateur Quartz - la syntaxe Spring CronTrigger n'autorise pas MON # 5 pour le cinquième lundi

Donc, la meilleure chose à faire est 0 0 3 29 2? qui n'exécutera qu'à 3h du matin le 29 février (années bissextiles)

6
mrmoosehead

Maintenant, lors de l'exécution de cette application localement sur mon propre ordinateur, je ne souhaite pas que ces travaux soient exécutés. Est-il possible d'écrire une expression cron qui ne se déclenchera jamais?

Si vous souhaitez désactiver la planification sur votre ordinateur, vous disposez de plusieurs moyens pour y parvenir.

Tout d'abord, vous pouvez déplacer la configuration de Quartz vers une configuration basée sur @Profile Sans activer ce profil localement. Quartz ne commencerait pas du tout si le profil n’est pas actif.

Une alternative consiste à configurer Quartz pour ne pas démarrer automatiquement. Il existe une SchedulerFactoryBean#setAutoStartup() que vous pouvez définir dans BeanPostProcessor enregistrée dans un profil dev. Alors que ce fil est assez ancien, Spring Boot offre une alternative en enregistrant un bean SchedulerFactoryBeanCustomizer pour faire la même chose.

2
Stephane Nicoll

Si vous utilisez l'expression dans une expression @Scheduled(cron="") (techniquement, elle n'utilise pas de quartz, mais elle est plutôt courante au printemps), vous ne pouvez pas utiliser la solution à 7 champs pour l'année à venir, mais ces options. :

  • Si vous utilisez spring 5.1+ (springBoot 2.1+), utilisez simplement "${your.cron.prop:-} Et ne définissez pas la propriété sur Désactiver - voir @ Scheduled
  • Désactivez le bean/service avec la méthode @Scheduled, Par exemple en utilisant une annotation @ConditionalOnProperty("my.scheduleproperty.active") et en ne définissant pas la propriété (ou en lui attribuant la valeur false)
1
icyerasor