Quartz: Cron expresión que nunca se ejecutará

Sé que hay un duplicado aquí , que probablemente sea exactamente mi caso, aunque merecería una explicación mejor, que trataré de presentar aquí.

Trabajo con una aplicación web Java usando un contexto de aplicación Spring. En este contexto, definí los trabajos progtwigdos usando Quartz. Estos trabajos son activados por un cron definido en un archivo .properties.

El contexto Spring está incrustado en la guerra, mientras que el archivo .properties está en el servidor de aplicaciones (Tomcat en este caso particular).

Esto está bien y permite definir diferentes crones según el entorno (desarrollo, integración, producción, …).

Ahora, cuando ejecuto esta aplicación localmente en mi propia computadora, no deseo que se ejecuten estos trabajos. ¿Hay alguna manera de escribir una expresión cron que nunca se disparará?

TL; DR

En Quartz 1, puedes usar este cron: 59 59 23 31 12 ? 2099 59 59 23 31 12 ? 2099 (última fecha válida).
En Quartz 2, puedes usar este cron: 0 0 0 1 1 ? 2200 0 0 0 1 1 ? 2200

Usando una expresión lejos en el futuro

Hizo algunas pruebas rápidas usando 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())); } 

Cuando hago String exp = "# 0 0 0 1 1 ?"; , la prueba isValid devuelve false .

Con la muestra anterior, el resultado es el siguiente:

 true null 

Sentido:

  • la expresión es válida;
  • no hay una fecha próxima que coincida con esta expresión.

Sin embargo, para que el planificador acepte un desencadenador cron, este último debe coincidir con una fecha en el futuro.

Intenté varios años y descubrí que una vez que el año está por encima de 2300, parece que Quartz ya no se molesta (aunque no encontré una mención a un valor máximo para el año en la documentación de Quartz 2 ). Puede haber una forma más limpia de hacerlo, pero esto satisfará mis necesidades por ahora.

Entonces, al final, el cron que propongo es 0 0 0 1 1 ? 2200 0 0 0 1 1 ? 2200 .

Variante de cuarzo 1

Tenga en cuenta que, en Cuarzo 1, 2099 es el último año válido . Por lo tanto, puede adaptar su expresión cron para usar la sugerencia de Maciej Matys : 59 59 23 31 12 ? 2099 59 59 23 31 12 ? 2099

Alternativa: Usar una fecha en el pasado

Arnaud Denoyelle sugirió algo más elegante, que mi prueba anterior valida como una expresión correcta: en lugar de elegir una fecha en un futuro lejano, elígela en un pasado lejano:

0 0 0 1 1 ? 1970 0 0 0 1 1 ? 1970 (la primera expresión válida según la documentación de Quartz).

Esta solución no funciona, sin embargo.

hippofluff destacó que Quartz detectará una expresión en pasado que nunca se ejecutará nuevamente y por lo tanto lanzará una excepción.

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

Esto parece haber estado en Quartz durante mucho tiempo .

Lecciones aprendidas: la prueba no es infalible como lo es

Esto resalta una debilidad de mi prueba: en caso de que quiera probar un CronExpression , recuerde que debe tener un nextValidTime 1 . De lo contrario, el progtwigdor al que lo pasará simplemente lo rechazará con la excepción mencionada anteriormente.

Aconsejaría adaptar el código de prueba de la siguiente manera:

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

Ahí tienes: no necesitas pensar, solo lee la salida.


1 Esta es la parte que olvidé cuando probé la solución de Arnaud, haciéndome el tonto y demostrando que mi prueba no era a prueba de nada.

Técnicamente, los valores válidos para el campo de año de cuarzo opcional son 1970-2099, por lo que 2300 no es un valor esperado. Supongo que realmente necesita hacer esto y su versión de Quartz intenta imponer la syntax cron válida (día 1-31, mes 1-12, etc.).

Actualmente estoy usando el siguiente código en Resque-scheduler for Rails, que acepta información de progtwigción en formato crontab validado, para crear un trabajo de prueba de ejecución manual:

 cron: "0 5 31 2 *" 

El trabajo esperará pacientemente durante la mañana del 31 de febrero antes de correr. Para un equivalente en crontrigger Quartz , pruebe esta línea o alguna variante de la misma:

 0 0 5 31 2 ? 

Prueba esto: 59 59 23 31 12 ? 2099 59 59 23 31 12 ? 2099

Encontré esto mientras trataba de resolver un problema similar, deshabilitando una expresión cron, pero me encontré con los mismos problemas de requerir una fecha de progtwigción futura válida.

También toco problemas usando la syntax de 7 valores: no puedo especificar un año en la progtwigción de cron.

Entonces usé esto: 0 0 3? 2 MON # 5

Las próximas veces que esto se ejecutará son:

  1. Lunes, 29 de febrero de 2044 3:00 a.m.
  2. Lunes, 29 de febrero de 2072 3:00 a.m.
  3. Lunes, 29 de febrero, 2112 3:00 a.m.
  4. Lunes, 29 de febrero, 2140 3:00 a.m.
  5. Lunes, 29 de febrero, 2168 3:00 a.m.

Entonces, esencialmente, a todos los efectos, está deshabilitado. 🙂

Ah. Maldiciones, esto solo funcionará para la syntax del planificador de Quartz: la syntax de Spring CronTrigger no permite MON # 5 para el quinto lunes

¿Entonces la próxima mejor cosa es 0 0 3 29 2? que solo se ejecutará a las 3 de la madrugada del 29 de febrero (años bisiestos)

Ahora, cuando ejecuto esta aplicación localmente en mi propia computadora, no deseo que se ejecuten estos trabajos. ¿Hay alguna manera de escribir una expresión cron que nunca se disparará?

Si desea deshabilitar la progtwigción en su computadora, tiene varias formas de hacerlo.

Primero, podría mover la configuración de Quartz a una configuración basada en @Profile y no habilitar este perfil localmente. Quartz no arrancaría si el perfil no está activo.

Una alternativa es configurar Quartz para que no se inicie automáticamente. Hay un SchedulerFactoryBean#setAutoStartup() que puede establecer en BeanPostProcessor registrado en un perfil dev. Si bien este hilo es bastante antiguo, Spring Boot ofrece una alternativa al registrar un bean SchedulerFactoryBeanCustomizer para hacer lo mismo.