Una Introducción a Quartz. Calendarización de Tareas en Java

Publicado en

Al desarrollar aplicaciones corporativas es común encontrarse con la necesidad de calendarizar tareas para que sean ejecutadas en automático cada cierto tiempo. Por ejemplo, podemos requerir que todos los días a medianoche se dispare un trabajo de sincronización de datos, o que el último día de cada mes se borren los archivos temporales de algún directorio. Las necesidades de agendar tareas pueden ser diversas y las aplicaciones que requieran de esto también serán muy distintas en tipo y en tamaño. Debido a este amplio rango de aplicación, no es raro encontrarnos con distintas herramientas de scheduling dentro de las cuales unas serán más adecuadas que otras para cubrir nuestras necesidades.

En el caso de aplicaciones desarrolladas con Java EE, podríamos pensar en las capacidades de scheduling que proveen los EJBs como primera opción. Conociendo que el desarrollo de una aplicación empresarial mediante EJBs pudiera no ser la opción mas adecuada para nuestras necesidades, podríamos apostar por desarrollar nuestra aplicación sobre un framework más ligero como Spring, y recurrir al API de Java Timer para resolver la funcionalidad de calendarización de tareas. Sin embargo, Java Timer solo es un API con funcionalidad básica para el manejo de tiempo, y se queda corto de lo que debería proveer un framework completo para calendarización.

Es aquí donde entra en juego Java Quartz. Quartz es un framework de scheduling open source que provee funcionalidad avanzada para la calendarización de tareas en Java. Entre estas están:

• Cualquier tarea escrita en Java es susceptible de ser agendada dentro del framework para ser ejecutada.
• Podemos agendar tareas en donde solo indiquemos la fecha y hora de ejecución y a partir de ahí definir una frecuencia de ejecución (ejemplo, ejecutar la tarea el primero de enero a las 12:00 a.m. y a partir de ahí, ejecutarse cada 3 días) hasta agendar tareas mediante el poder de las expresiones de Cron (ejemplo, ejecutar la tarea el segundo lunes de los meses de enero a septiembre del año 2008 a partir de las 8:00 a.m. cada 15 min. hasta las 10:00 a.m.).
• Definir un medio de persistencia donde se almacene la información de tareas y sus agendas para poder darle la posibilidad al framework de recuperar dicha información ante fallas de la aplicación.
• Capacidad de trabajar en un ambiente de clusters.

Con esta funcionalidad, es claro que Quartz es una excelente opción que nos proporciona un framework robusto de scheduling, con integración en aplicaciones empresariales que requieran de alta disponibilidad mediante sus características de persistencia y clustering, adicionalmente ofreciendo la posibilidad de integrarlo con cualquier framework sobre el cual se desarrolle nuestra aplicación.

Un ejemplo sencillo
Después de haber comentado acerca de lo que es Quartz y las características que nos ofrece, el siguiente paso es aplicar Quartz en un ejemplo práctico y qué otra manera de iniciar un ejemplo de este tipo sino es con el clásico “¡Hola, mundo!”.

Cualquier tarea escrita en Java es susceptible de ser agendada dentro de Quartz, con el único requisito de que la clase que desarrolle dicha tarea implemente la interfaz org.quartz.Job la cual define un método execute que luce de la siguiente manera:

public void execute(JobExecutionContext context) throws JobExecutionException;

Después de hacer que nuestra clase que desarrolla la tarea que deseamos agendar implemente la interfaz Job, el siguiente paso sería preparar el ambiente de ejecución, lo cual consiste en:
1. Crear un Job a partir de esta clase
2. Asociarle un objeto de tipo Trigger (donde indicaremos cada cuándo la tarea deberá ser ejecutada)
3. Obtener una instancia de la clase Scheduler donde registraremos el Job creado.

El listado 1 muestra un ejemplo de la implementación de la interfaz Job, mientras que el listado 2 muestra como se prepara el ambiente de ejecución.

package quartzdemo;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HolaMundoJob implements Job{

public void execute(JobExecutionContext jobExecutionContext)
throws JobExecutionException {
System.out.println(“¡Hola, mundo! :D”);
}

}

Listado 1. Implementación de la interfaz Job

package quartzdemo;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.helpers.TriggerUtils; import org.quartz.impl.StdSchedulerFactory;
public class Test {

public static void main(String[] args) {
try {
// Creacion de una instacia de Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
System.out.println(“Iniciando Scheduler...”);
scheduler.start();
// Creacion una instacia de JobDetail
JobDetail jobDetail = new JobDetail(
“HolaMundoJob”,
Scheduler.DEFAULT_GROUP,
HolaMundoJob.class);

// Creacion de un Trigger donde indicamos
//que el Job se
// ejecutara de inmediato y a partir de ahi en lapsos
// de 5 segundos por 10 veces mas.
Trigger trigger = new SimpleTrigger(
“HolaMundoTrigger”,
Scheduler.DEFAULT_GROUP,
10, 5000);

// Registro dentro del Scheduler
scheduler.scheduleJob(jobDetail, trigger);

// Damos tiempo a que el Trigger registrado
//termine su periodo
// de vida dentro del scheduler
Thread.sleep(60000);

// Detenemos la ejecución de la
// instancia de Scheduler
scheduler.shutdown();

} catch(Exception e) {
System.out.println(“Ocurrió una excepción”);
}
}

}


Listado 2. Preparación del ambiente de ejecución

Al ejecutar este código veremos en consola un resultado parecido al siguiente:

30/09/2007 01:45:27 PM org.quartz.simpl.SimpleThreadPool initialize
INFO: Job execution threads will use class loader of thread: main
30/09/2007 01:45:27 PM org.quartz.simpl.RAMJobStore initialize
Iniciando Scheduler...
INFO: RAMJobStore initialized.
30/09/2007 01:45:27 PM org.quartz.impl.StdSchedulerFactory instantiate
INFO: Quartz scheduler ‘DefaultQuartzScheduler’ initialized from default resource file in Quartz package: ‘quartz.properties’
30/09/2007 01:45:27 PM org.quartz.impl.StdSchedulerFactory instantiate
INFO: Quartz scheduler version: 1.4.5
30/09/2007 01:45:27 PM org.quartz.core.QuartzScheduler start
INFO: Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
¡Hola, mundo! :D

¡Hola, mundo! :D
¡Hola, mundo! :D
¡Hola, mundo! :D
¡Hola, mundo! :D
¡Hola, mundo! :D
¡Hola, mundo! :D
¡Hola, mundo! :D
¡Hola, mundo! :D
¡Hola, mundo! :D
30/09/2007 01:46:27 PM org.quartz.core.QuartzScheduler shutdown
INFO: Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
30/09/2007 01:46:27 PM org.quartz.core.QuartzScheduler pause
INFO: Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
30/09/2007 01:46:27 PM org.quartz.core.QuartzScheduler shutdown
INFO: Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.

En los mensajes arrojados por la consola, nos daremos cuenta que al principio aparece la información de inicialización de la instancia de Scheduler que obtuvimos, posterior a estos encontraremos los “¡Hola, mundo!” producto de las ejecuciones de nuestro Job que registramos dentro del Scheduler y finalmente veremos los mensajes que arroja la finalización de la instancia de Scheduler.

Como mencioné anteriormente, Quartz soporta el manejo de expresiones de Cron que permiten hacer agendas más complejas que tan solo indicar un número de lapsos y periodo de ejecución para un Job. Imaginemos el caso de una empresa que tiene un proceso para calcular el nivel de producción que se tuvo en un mes, por lo que quisiéramos ejecutar este proceso en automático el último día de cada mes a las 6:30 pm que es la hora en que se termina la jornada laboral.

El cambio a realizar en nuestro ejemplo, sería en la forma de crear el Trigger. Ahora en lugar de hacer una instancia de SimpleTrigger crearíamos una instancia de CronTrigger de la siguiente manera:

 

Trigger trigger2 = new CronTrigger(“HolaMundoTrigger”,
Scheduler.DEFAULT_GROUP,
“0 30 6 L * ?”);

La intención aquí no es explicar como funcionan las expresiones de Cron, simplemente quiero hacer ver las posibilidades que ofrece Quartz. Quartz puede ser aprovechado tanto por aplicaciones muy sencillas, como por aplicaciones empresariales de misión crítica que mantengan una alta disponibilidad.

Los invito a conocer más sobre Quartz en http://www.opensymphony.com/quartz/

Referencias:
• Chuck Cavaness. “Quartz Job Scheduling Framework”. Prentice Hall.

Bio

Erick Frausto se desempeña como Arquitecto Java EE para ISOL (Ingeniería de Soluciones). Actualmente está trabajando en un proyecto para Afores en conjunto con la empresa EFP (Especialistas en Fondos de Previsión Social) donde está aplicando Quartz. Es egresado de Ing. en Informática de UPIICSA-IPN, es desarrollar Java certificado por Sun. Erick dedica este artículo a su familia y a las hermosas personas que ha encontrado en su camino.