Planificación de tareas en Java mediante Quartz
- Introducción
- Características principales
- Principales clases e interfaces
- Ejemplo. Planificación de tareas en modo programático.
- Ejemplo. Planificación de tareas en modo declarativo.
- Aplicación Web para gestionar nuestras tareas (modo declarativo)
- Direcciones de interés (en inglés)
- Conclusiones
Introducción
Normalmente los negocios requieren que en determinados momentos se ejecuten tareas de forma automática, por ejemplo: "todos los viernes a las 15:00 enviar informes a clientes".Crear un sistema robusto y completo que de soporte a la ejecución de esas tareas, además de no ser una tarea fácil, sería como reinventar la rueda pues ya disponemos de frameworks maduros de libre uso como el que vamos a presentar en este tutorial.
Quartz es un framework open source, con licencia Apache 2.0 para la planificación y gestión de tareas.
Es usado activamente en conocidos proyectos y organizaciones como JBoss, Cocoon, Apache Jakarta,... ver más
Características principales
- Válido para aplicaciones tanto J2EE como J2SE.
- Planificación flexible de tareas, por ejemplo: el primer lunes de Enero de cada año a las 17:45.
- Mantenimiento del estado de las tareas incluso en caso de fallos y reinicios de máquinas.
- Posibilidad de participar en transacciones JTA.
- Posibilidad de trabajar en modo Clúster.
- Proporciona un completo API, con muchas clases de utilidad y muchos tipos Listener (JobListener, TriggerListener y SchedulerListener).
Principales clases e interfaces
A continuación describimos los componentes que lo compomen:Job, JobDetail, Trigger, JobStore, Scheduler
.org.quartz.Job
Definir una tarea es tan sencillo como implementar la interface org.quartz.Job
cuya definición se muestra a continuación:- package org.quartz;
- public interface Job {
- public void execute(JobExecutionContext context) throws JobExecuteException;
- }
org.quartz.JobDetail
Es una clase que almacena propiedades de una determinada tarea.Las tareas se clasifican en grupos de tareas y cada tarea tiene un nombre único dentro del grupo. JobDetail define estás y otras propiedades.
Gracias a esta clasificación podemos pausar, iniciar, detener, etc. tareas o grupos de tareas de manera independiente al resto.
org.quartz.Trigger
Es una clase abstracta que define los instantes en que la tarea debe ser ejecutada, por ejemplo todos los lunes a las 16:00 de la tarde.Existen varias implementaciones de esta clase pero las más usadas son:
org.quartz.SimpleTrigger
Permite especificar ejecuciones de tareas teniendo en cuenta los siguientes parámetros: fecha, hora, nº de repeticiones e intervalo entre repeticiones. Por ejemplo, ejecutar la tarea X 3 veces el día 13/12/1976 a las 13:30 con 40 minutos entre cada ejecución.
Para que se haga una idea, la firma de uno de sus constructores es:
- public SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval)
org.quartz.CronTrigger
Es el más utilizado, pues permite especificar mediante expresiones más complejas los instantes en los que deben ejecutarse las tareas. Por ejemplo, todos los días 14 que caigan en jueves cada 5 minutos desde las 14:00 hasta las 18:00.
Aunque existe mucha funcionalidad de utilidad que permiten hacernos la vida más fácil a la hora de crear el CronTrigger, yo suelo usar un constructor que acepta una cadena de texto representando una expresión de tipo Cron de Unix: segundos, minutos, horas, días del mes, meses, días de la semana, [año] (observe que todos son obligatorios a excepción de el año).
"0 0 23 * * ?" | La tarea será ejecutada todos los días a las 23:00 |
---|---|
"0 20 15 * * 1 2007" | La tarea será ejecutada todos los domingos del año 2007 a las 15:20 |
CronTrigger
haga clic aquí.org.quartz.JobStore
Interface para manejar el almacenamiento y recuperación de la información de planificación (tareas, triggers, etc).Implementaciones de
org.quartz.JobStore
:org.quartz.simpl.RAMJobStore | Es la implementación de por defecto, almacena la información en memoria RAM por lo que cuando la aplicación finaliza no se guarda el estado. |
org.quartz.impl.jdbcjobstore.JobStoreTX | Implementación que almacena la información en una dase de datos a través de JDBC. Diseñado para entornos no transaccionales. |
org.quartz.impl.jdbcjobstore.JobStoreCMP | Implementación que almacena la información en una dase de datos a través de JDBC. Diseñado para entornos transaccionales. |
org.quartz.jobStore.class
de el archivo de propiedades de Quartz quartz.properties
, por ejemplo.org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
org.quartz.Scheduler
Su funcionalidad es almacenar y planificar las tareas (Job) en base a los Triggers, recuperar tareas fallidas, realizar reintentos y gestionar el estado del sistema de planificación.Ejemplo. Planificación de tareas en modo programático.
A continuación y a modo de ejemplo vamos a crear una aplicación que lance una tarea cada 5 segundos.Aunque la tarea podría realizar cualquier operación compleja, en este caso simplemente mostrará un mensaje por pantalla.
com.autentia.tutoriales.quartz.StatusMonitorJob.java
Implementación de la tarea:- package com.autentia.tutoriales.quartz;
- /**
- * Tarea de ejemplo.
- * Guarda en un archivo la memoria total y libre de la máquina virtual
- * @author Carlos García. Autentia Real Business Solutions
- * @see http://www.mobiletest.es
- */
- public class StatusMonitorJob implements org.quartz.Job {
- public static final String TARGET_FILE_PROP = "outputFile";
- /*
- * La tarea es ejecutada
- * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
- */
- public void execute(org.quartz.JobExecutionContext context) throws org.quartz.JobExecutionException {
- org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(StatusMonitorJob.class);
- if (logger.isDebugEnabled()){
- logger.debug("Executing");
- }
- StringBuffer statusLine = new StringBuffer(128);
- java.io.FileWriter writer = null;
- // Obtenemos la propiedad que especifica el archivo donde se desea guardar el estado de la VM, proporcionando un
- // valor por defecto en caso de que no se haya especificado
- org.quartz.JobDataMap properties = context.getJobDetail().getJobDataMap();
- String outputFile = properties.getString(StatusMonitorJob.TARGET_FILE_PROP);
- if ((outputFile == null) || ("".equals(outputFile.trim()))){
- outputFile = "status.txt";
- }
- try {
- // Escribimos en el archivo una linea de estado con formato: INSTANTE TOTAL_MEMORY FREE_MEMORY
- writer = new java.io.FileWriter(outputFile, true);
- statusLine.append(System.currentTimeMillis());
- statusLine.append('\t');
- statusLine.append(Runtime.getRuntime().totalMemory());
- statusLine.append('\t');
- statusLine.append(Runtime.getRuntime().freeMemory());
- statusLine.append(System.getProperty("line.separator"));
- if (logger.isDebugEnabled()){
- logger.debug("Storing VM state to file: " + outputFile);
- }
- writer.write(statusLine.toString());
- } catch (java.io.IOException e) {
- logger.error("Error ", e);
- } finally {
- try {
- writer.close();
- } catch (Exception ex){}
- }
- }
- }
com.autentia.tutoriales.quartz.StatusMonitor.java
Aplicación de escritorio que realiza la planificación de la tarea:- package com.autentia.tutoriales.quartz;
- /**
- * Planificación de tareas en Java mediante Quartz
- * @author Carlos García. Autentia Real Business Solutions
- * @see http://www.mobiletest.es
- */
- public class StatusMonitor {
- private org.quartz.Scheduler scheduler;
- /**
- * Instancia, configura e inicia la tarea para ser ejecutada cada 5 segundos.
- * @throws org.quartz.SchedulerException
- */
- public void start() throws org.quartz.SchedulerException {
- try {
- // Definimos la tarea (nombre de la tarea, nombre del grupo de tareas, Clase que implementa la tarea)
- org.quartz.JobDetail jobDetail = new org.quartz.JobDetail("StatusJob", org.quartz.Scheduler.DEFAULT_GROUP, StatusMonitorJob.class);
- // Configuramos los parametros de la tarea, en esta caso le decimos en que archivo debe guardar el estado de la VM.
- jobDetail.getJobDataMap().put(StatusMonitorJob.TARGET_FILE_PROP, "vm_status.txt");
- // Configuramos el Trigger que avisará al planificador de cuando debe ejecutar la tarea, en este caso cada 5 segundos.
- org.quartz.CronTrigger trigger = new org.quartz.CronTrigger("StatusTrigger", org.quartz.Scheduler.DEFAULT_GROUP, "0/5 0 0 * * * ?");
- // Obtenemos el planificador
- scheduler = org.quartz.impl.StdSchedulerFactory.getDefaultScheduler();
- // La tarea definida en JobDetail será ejecutada en los instantes especificados por el Trigger.
- scheduler.scheduleJob(jobDetail, trigger);
- // Iniciamos las tareas planificadas en el Sheduler
- scheduler.start();
- } catch (java.text.ParseException e) {
- // No se dará
- }
- }
- /**
- * Detiene el proceso de planificación
- */
- public void stop(){
- try {
- scheduler.shutdown();
- } catch (Exception ex) {
- // Nada
- }
- }
- /**
- * Punto de entrada a la aplicación
- */
- public static void main(String[] args) {
- StatusMonitor monitor = new StatusMonitor();
- try {
- monitor.start();
- System.out.println("Pausa... pulse una tecla para finalizar la aplicación");
- System.in.read();
- monitor.stop();
- } catch (Exception ex) {
- System.err.println(ex);
- }
- }
- }
Ejemplo. Planificación de tareas en modo declarativo.
Por defecto, en la configuración de Quartz puede especificar tareas externamente a la aplicación en un archivo de nombrequartz_jobs.xml
Este método le ofrece una gran flexibilidad, ya que podrá añadir o eliminar tareas sin realizar la gestión de planificación dentro de la aplicación... simplemente deberá copiar las clases en el CLASSPATH de la máquina virtual Java.
quartz_jobs.xml
- xml version="1.0" encoding="utf-8"?>
- <quartz>
- <job>
- <job-detail>
- <name>QuartzTarea1name>
- <group>DEFAULTgroup>
- <description>Esta es una tarea de ejemplo del uso de Quartzdescription>
- <job-class>com.autentia.tutoriales.quartz.MyTaskjob-class>
- <volatility>falsevolatility>
- <durability>falsedurability>
- <recover>falserecover>
- <job-data-map allows-transient-data="true">
- <entry>
- <key>propiedad1key>
- <value>valor_propiedad1value>
- entry>
- job-data-map>
- job-detail>
- <trigger>
- <simple>
- <name>QuartzTrigger1name>
- <group>DEFAULTgroup>
- <job-name>QuartzTrigger1job-name>
- <job-group>DEFAULTjob-group>
- <start-time>2005-09-17 0:10:00 PMstart-time>
- <repeat-count>-1repeat-count>
- <repeat-interval>1000repeat-interval>
- simple>
- trigger>
- job>
- quartz>
Aplicación Web para gestionar nuestras tareas (modo declarativo)
En el siguiente enlace http://prdownloads.sourceforge.net/quartz/quartz-web-app.zip puede obtener una útil aplicación Web para gestionar las tareas a través de una completa aplicación Web. Su uso queda fuera del alcance de este tutorial....Los datos de acceso de por defecto son
quartz
tanto para el nombre de usuario como para la contraseña.Direcciones de interés (en inglés)
- Página principal del proyecto Quartz
- Tutorial de Quartz
- ¿Qué es Quartz?
- Job Scheduling in Java
- Using Quartz Scheduler in a Java EE Web Application
- The Quartz web application
Conclusiones
Quartz es un FrameWork maduro y muy extendido, que nos proporciona ampliar la funcionalidad de nuestros proyectos que basan en tareas de una forma fácil y flexible.Sin lugar a dudas, Quartz es una opción a tener en cuenta en estos casos... Para más información diríjase a la documentación del proyecto.
Comentarios