Configuración de expresiones en Quartz

 

Quartz es un framework o librería que podemos utilizar para indicar, de una manera fácil, cada cuanto queremos que se ejecute un proceso determinado.

Se debe usar Quartz en conjunto con Spring. Es a través de Spring como se logra injectar a través de configuración los objetos, que realizan la ejecución del proceso determinado.

Lo que hace Quartz es posibilitar la configuración de expresiones o la indicación por medio de propiedades de la periodicidad con que se ejecutará el proceso definido y es en últimas el encargado de la ejecución periodica. Haciendo uso de Quartz ya no es necesario, utilizar cosas como Timers y manejarlas nosotros mismos, por lo cual nos vuleve más productivos.

A continuación  un ejemplo de consola, pero antes una breve explicación, del conflicto de versiones de asemblies que se les puede presentar, como de hecho se me presentó a mi.

Se deben usar los asemblies de Spring:

Spring.Aop.dll

Spring.Core.dll

Spring.Data.dll

Spring.Scheduling.Quartz.dll

También se requiere usar la librería:

Common.Logging.dll ( que viene en la distribución o download de las librerías de Spring).

Finalmente, se debe referenciar el Assembly de Quartz:

Quartz.dll

Aca lo importante, para evitar conflictos, es fijarse en las versiones. En mi caso, cuando por fin logré superar el conflicto de versiones, estas son las versiones de los assemblies:

Assemblies de Spring, versión 1.3.1.40711.

Assemblie Common.Logging, versión 1.2.0.0

Assemblie Quartz, versión 1.0.3.3

Teniendo esto claro, ahora así el ejemplo de consola:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Presione enter para ingresar el número a través del servicio");
        Console.ReadKey();

      
try
       
{
            XmlApplicationContext ctx = new XmlApplicationContext("spring-objects.xml");
            Console.WriteLine("Spring configuración exitosa, qurtz jobs running");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            Console.Out.WriteLine("---Presione enter para salir---");
            Console.ReadLine();
        }

        Console.Out.WriteLine("---Presione enter para salir---");
        Console.ReadLine();

    }
}

El programa consiste entonces en usar la clase XmlApplicationContext de Spring, para ejecutar en dicho contexto, los objetos configurados declarativamente en el archivo spring-objects.xml.

El archivo de objetos, en este caso, posee la siguiente declaración de objetos:

<?xml version="1.0" encoding="utf-8" ?>
<
objects xmlns="http://www.springframework.net"
xmlns:aop="http://www.springframework.net/aop"
xmlns:db="http://www.springframework.net/database"
xmlns:tx="http://www.springframework.net/tx">
<!--
Scheduling Task -->
<
object name="MyJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz">
<
property name="JobType" value="PruebaCronTrigger.MyJob, PruebaCronTrigger"/>
<
property name="JobDataAsMap">
<
dictionary>
<
entry key="UserName" value="Ingecaam"></entry>
</
dictionary>
</
property>
</
object>

<
object id="JobService" type="PruebaCronTrigger.JobService, PruebaCronTrigger">
<
property name="UserName" value="Ingecaam"/>
</
object>

<
object id="jobDetail" type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<
property name="TargetObject" ref="JobService" />
<
property name="TargetMethod" value="DoJobWork" />
<
property name="Concurrent" value="false" />
</
object>

<
object id="JobSimpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<
property name="jobDetail" ref="jobDetail" />
<
property name="startDelay" value="5s" />
<
property name="repeatInterval" value="5s" />
</
object>

<
object id="JobCronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz">
<
property name="jobDetail" ref="MyJob" />
<
property name="cronExpressionString" value="0/20 * * * * ?" />
</
object>

<
object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<
property name="triggers">
<
list>
<
ref object="JobCronTrigger" />
<!--
<ref object="JobSimpleTrigger" />-->
</
list>
</
property>
</
object>
</
objects>

Aquí, hay varias cosas a explicar:


1. Lo que hace Quartz es ejecutar las tareas que se le definan, las tareas se le definen en el objeto SchedulerFactoryObject:

<object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<
property name="triggers">
<
list>
<
ref object="JobCronTrigger" />
<!--
<ref object="JobSimpleTrigger" />-->
</
list>
</
property>
</
object>
En el ejemplo, solo se le define la tarea asociada a un objeto del tipo CronTriggerObject.

2. Las tareas que se definen pueden ser de 2 tipos CronTriggerObject y SimpleTriggerObject.


SimpleTriggerObject

<object id="JobSimpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
<
property name="jobDetail" ref="jobDetail" />
<
property name="startDelay" value="5s" />
<
property name="repeatInterval" value="5s" />
</
object>

CronTriggerObject

<object id="JobCronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz">
<
property name="jobDetail" ref="MyJob" />
<
property name="cronExpressionString" value="0/20 * * * * ?" />
</
object>

Como vemos, al utilizar un SimpleTriggerObject podemos de una manera sencilla configurar por medio de propiedades (startDelay y repeatInterval) la periodicidad con la que queremos que se ejecute la tarea, en el caso del ejemplo cada 5 segundos.


No obstante también disponemos del CronTriggerObject que es más robusto y nos permite por medio de expresiones, definir una periodicidad basada en calendario, se pueden especificar periodicidades como: todos los viernes al mediodía", o "todos los días laborables y de 9:30 am", o incluso "cada 5 minutos 09 a.m.-10 a.m. todos los lunes, miércoles y viernes durante enero".


3. En la propiedad ref de ambos tipos de tarea, se indican los objetos donde se configuran los objetos y métodos que ejecutan las tareas dadas.


Por ejemplo, para el SimpleTriggerObject tenemos:

<object id="jobDetail" type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz">
<
property name="TargetObject" ref="JobService" />
<
property name="TargetMethod" value="DoJobWork" />
<
property name="Concurrent" value="false" />
</
object>

Como se puede ver, se configura el objeto y el método que se ejecuta periodicamente. El método es el DoJobWork y el objeto es el que se configura en este otro objeto declarado de Spring:

<object id="JobService" type="PruebaCronTrigger.JobService, PruebaCronTrigger">
<
property name="UserName" value="Ingecaam"/>
</
object>

Aca sí se configura el objeto y además se le puede asignar valor a una de sus propiedades, el objeto JobService en este caso es el del siguiente código:

namespace PruebaCronTrigger
{
public class JobService
{
private string _username;

public string Username
{
get { return _username; }
set { _username = value; }
}

public void DoJobWork()
{
Console.WriteLine("{0} DoJobWork llamado, user name: {1}", DateTime.Now, _username);
}
}
}

Vemos como posee la propiedad UserName, el método DoJobWork  y el nombre de su namespace es PruebaCronTrigger.


Cuando en la lista de las tareas, solo se configura el SimpleTriggerObject:

<object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<
property name="triggers">
<
list>
<!--
<ref object="JobCronTrigger" />-->
<
ref object="JobSimpleTrigger" />
</
list>
</
property>
</
object>

La salida del programa es:


1


Para el caso del CronTriggerObject el objeto que se referencia es:

<object name="MyJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz">
<
property name="JobType" value="PruebaCronTrigger.MyJob, PruebaCronTrigger"/>
<
property name="JobDataAsMap">
<
dictionary>
<
entry key="UserName" value="Ingecaam"></entry>
</
dictionary>
</
property>
</
object>

Y en este caso, el objeto con nuestro código que hará el proceso que se realizará periodicamente, es el siguiente:

namespace PruebaCronTrigger
{
public class MyJob : QuartzJobObject
{
private string _userName;

public string UserName
{
get { return _userName; }
set { _userName = value; }
}

protected override void ExecuteInternal(Quartz.JobExecutionContext context)
{
Console.WriteLine("{0}: ExecuteInternal llamado, user name:{1}, next fire time {2}", DateTime.Now, _userName, context.NextFireTimeUtc.Value.ToLocalTime());
}



}
}

En este caso, lo que se ejecuta periodicamente, es lo que se coloqué en la sobreescritura del método ExecuteInternal. La periodicidad se define por medio de una expresión en la propiedad cronExpressionString del objeto CronTriguerObject.

<object id="JobCronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz">
<
property name="jobDetail" ref="MyJob" />
<
property name="cronExpressionString" value="0/20 * * * * ?" />
</
object>

En este caso la expresión configurada de periodicidad es esta:


0/20 * * * * ?


Lo que significa cada 20 segundos, por tanto, si en la lista de tareas solo se configura la tarea asociada al CronTriggerObject:

<object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
<
property name="triggers">
<
list>
<
ref object="JobCronTrigger" />
<!--
<ref object="JobSimpleTrigger" />-->
</
list>
</
property>
</
object>

La salida del programa es:


2


Adicionalmente, podemos configurar varias tareas para ejecutar en difentes momentos y las podemos conbinar de los 2 tipos. Esto da aún más flexibilidad y nos ayuda a contruir aplicaciones, donde debemos lanzar procesos de forma periodica.


Por ejemplo, al configurar ambas tareas, la del BasicTriggerObject y la del CronTriggerObject:









<object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
  <
property name="triggers">
    <
list>
      <
ref object="JobCronTrigger" />
      <
ref object="JobSimpleTrigger" />
    </
list>
  </
property>
</
object>


La salida del programa es esta:


3


Nota: para que el ejemplo te funcione, asegurate que el archivo spring-objects.xml tenga definido el BuildAction a Content.


La expresión de periodicidad usada en este ejemplo es:


0/20 * * * * ?


Esto significa cada 20 segundos. Es importante, entonces, a la hora de necesitar configurar una periodicidad conocer muy bien como se construyen este tipo de expresiones. A continuación, una guía.


Expresiones Cron


Las expresiones Cron se utilizan para configurar las instancias de CronTrigger. Son cadenas que en realidad están hechas de siete sub-expresiones, que describen los detalles particulares de la programación. Estas sub-expresiones se separan por un espacio en blanco, y representan:

1. Segundos

2. Minutos

3. Horas

4. Día del mes

5. Mes

6. Día de la semana

7. Año (campo opcional)

Un ejemplo de una cron-expresión completa es la cadena "0 0 12 * WED" - que significa "todos los miércoles a las 12:00:00 pm".

Las sub-expresiones individuales pueden contener rangos y / o listas. Por ejemplo, el campo asociado al día de la semana en el ejemplo anterior (que se lee "WED") podría sustituirse por "MON-FRI", "MON, WED, FRI", o incluso "MON-WED, SAT".

Caracteres comodín (el *) se puede utilizar para decir "todo" posible valor de este campo. Por lo tanto, el carácter * en el campo "Mes" del ejemplo anterior simplemente significa "cada mes". Un '*' en el campo el día de la semana por lo tanto, obviamente, significa "todos los días de la semana".

Todos los campos tienen un conjunto de valores válidos que se pueden especificar. Estos valores podrían ser bastante obvios - como los números de 0 a 59 para el segundo y minutos, y los valores de 0 a 23 por hora. Día de mes puede ser cualquier valor de 1 a 31, pero hay que tener cuidado con la cantidad de días hay en un mes!. Los meses se pueden especificar como valores de entre 0 y 11, o mediante el uso de las cadenas JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV y DEC. Los días de la semana se puede especificar como valores entre 1 y 7 (1 = domingo) o mediante el uso de las cadenas SUN, MON, TUE, WED, THU, FRI y SAT.

El carácter '/' se puede utilizar para especificar incrementos en los valores. Por ejemplo, si pones ‘0 / 15’ en el campo de los minutos, que significa “cada 15 minutos de la hora, a partir del minuto cero”. Si se usa '3 / 20 'en el campo Minutos, significaría “cada 20 minutos de la hora, a partir del minuto 3” - o en otras palabras, es lo mismo que especificar '3, 23,43' en el campo Minutos. Tenga en cuenta la sutileza al usar estas expresiones; ‘/ 35’ no quiere decir "cada 35 minutos" - significa "cada 35 minutos de la hora, a partir de minuto cero" - en otras palabras, lo mismo que especificar '0, 35 '.

El '?' carácter está permitido para los campos de día de la semana y día de mes. Se utiliza para especificar "sin valor específico". Esto es útil cuando tiene que especificar algo en uno de los dos campos, pero no a la inversa. Vea los ejemplos a continuación para aclaración.

El carácter 'L' se permite para los campos de día de la semana y día de mes. Este caracterer es la manera corta de indicar “last”, pero tiene un significado diferente en cada uno de los campos. Por ejemplo, el valor "L" en el campo día de mes significa "el último día del mes" - día 31 de enero, el día 28 de febrero en años no bisiestos. Si se utiliza en el campo de día de la semana solo sin estar precedido por ningún valor, simplemente significa "7" o "SAT". Pero si se utiliza en el campo el día de la semana después de otro valor, significa "el último día del mes xxx" - por ejemplo, "6L" o "FRIL" significa tanto "el último viernes de cada mes". También puede especificar un desplazamiento desde el último día del mes, como "L-3", lo que significaría el tercer al último día del mes calendario. Cuando se utiliza la opción 'L', es importante que no se especifique listas o rangos de valores, ya que se pueden obtener resultados confusos o inesperados.

La 'W' se utiliza para especificar el día de la semana (lunes a viernes) más cercano al día. Por ejemplo, si usted tuviera que especificar "15W" como valor para el campo del día de mes, el significado es: "el día de la semana más cercano al día 15 del mes".

El "#" se utiliza para especificar "el enésimo" XXX día de la semana del mes. Por ejemplo, el valor del "6 # 3" o "FRI # 3" en el campo el día de la semana significa "el tercer viernes de cada mes".

He aquí unos cuantos ejemplos de expresiones y sus significados.

 

Ejemplo de expresiones Cron


Ejemplo CronTrigger 1 - expresión para crear un disparador (Trigger) que simplemente se dispara cada 5 minutos

"0 0/5 ***?"

CronTrigger Ejemplo 2 - expresión para crear un disparador que se activa cada 5 minutos, a los 10 segundos después del minuto (es decir, 10:00:10 am, 10:05:10 am, etc.)

"10 0/5 ***?"

Ejemplo CronTrigger 3 - expresión para crear un disparador que se activa a las 10:30, 11:30, 12:30 y 13:30, todos los miércoles y viernes.

"0 30 10-13? * WED,FRI"

CronTrigger Ejemplo 4 - expresión para crear un disparador que se activa cada media hora entre las horas de 8 am y las 10 am los días 5 y 20 de cada mes. Tenga en cuenta que el disparador no se dispara a las 10:00 am, justo a las 8:00, 08:30, 09:00 y 09:30

"0 0/30 8-9 5,20 *?"

CronTrigger Ejemplo 5.

Expresión para cumplir con el requerimiento:

Ejecutar el trabajo de lunes a viernes a las 6 am y 2 pm.

"0 0 6,14 * * MON, TUE, WED, THU, FRI"

CronTrigger Ejemplo 6.

Expresión para cumplir con el requerimiento:

Ejecutar el trabajo los sábados: a las 6 am y 10 am.

"0 0 6,10 * * SAT"

Tenga en cuenta que algunos de los requisitos de programación son demasiado complicados para expresarlos con un solo trigger - por ejemplo, "cada 5 minutos 09 a.m.-10 a.m., y cada 20 minutos 13:00-22:00". La solución en este caso es simplemente crear dos disparadores, y registrar los dos para ejecutar el mismo trabajo.

Comentarios

Entradas populares de este blog

Visual Studio 2012 Backup and Restore bases de datos

Hacer que Windows XP luzca como Windows 7