jueves, marzo 18, 2010

Uso de Quartz para tareas programadas en Tomcat

Tomado de:
http://www.javisjava.com/blog/


Quartz
19 Comments Published Junio 29th, 2008 in General

Quartz es un programador de tareas en Java. Para empezar, hay que decir que J2SE ya viene de serie con soporte para la programación de tareas, gracias a las clases Timer y TimerTask, que son capaces de lanzar un hilo por cada tarea que se ejecuta.
Sin embargo, Quartz va más allá, pues aporta un framework mucho más completo, y con características interesantes, como:

* Configuración declarativa (ficheros XML o properties), además de programática.
* Multitud de opciones de programación (por ejemplo, sintaxis tipo cron).
* Mantenimiento de estado de las tareas.
* Potente gestión de errores o tareas incompletas recuperables.
* Disponibilidad de Listeners no intrusivos sobre las tareas, los disparadores o sobre el propio programador.
* Integración con aplicaciones web vía Servlet o ContextListener.
* Integración con Spring.
* Soporte a entorno distribuido (cluster).

Es muy común la necesidad de dar de alta procesos periódicos asociados a una aplicación web. Quartz es especialmente útil precisamente porque elimina la obligación de tener crones de Linux o tareas programadas de Windows, de forma que toda nuestra aplicación, con la funcionalidad completa, reside en el WAR.
Estos son los pasos para dar de alta una tarea en una aplicación web de la forma más sencilla.

1. Se descarga Quartz, y se introducen en el WEB-INF/lib de la aplicación el JAR del propio Quartz y todos de los que depende (están incluidos en la distribución).
2. En el fichero web.xml se da de alta un listener, QuartzInitializerListener. Se trata de un ContextListener, que ejecuta automáticamente un método en el arranque de la aplicación (arranca el programador de tareas) y otro en la parada (obviamente, para el programador).


<web-app ... >
    <display-name>QuartzExample</display-name>
    <listener>
        <listener-class>org.quartz.ee.servlet.QuartzInitializerListener</listener-class>
    </listener>
</web-app>





3. Una vez arrancado, el programador buscará su configuración. Por defecto, deberá estar en un fichero de nombre quartz.properties en el raíz del classpath (WEB-INF/classes). El contenido de este fichero para una tarea simple, sin gestión del estado, soporte para cluster, etc, sería:

# Programador #
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
# Hilos #
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
# Almacenamiento #
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
# Plugins #
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
org.quartz.plugin.jobInitializer.overWriteExistingJobs = true
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
org.quartz.plugin.jobInitializer.validating=false


4. Las últimas líneas del quartz.properties están configurando un plugin capaz de incorporar las tareas y disparadores de un fichero XML. De nuevo, usaremos convención sobre configuración, de modo que llamaremos a ese fichero quartz_jobs.xml, y también lo situaremos en WEB-INF/classes, con el siguiente contenido:
<quartz ... >
    <job>
        <job-detail>
            <name>QuartzExampleJob</name>
            <group>ExampleGroup</group>
            <description>ExampleJob</description>
            <job-class>com.javisjava.quartz.jobs.QuartzExampleJob</job-class>
            <job-data-map allows-transient-data="false">
                <entry>
                    <key>nombre</key>
                    <value>Javier</value>
                </entry>
            </job-data-map>
        </job-detail>
        <trigger>
            <cron>
                <name>QuartzExampleTrigger</name>
                <group>ExampleTriggerGroup</group>
                <job-name>QuartzExampleJob</job-name>
                <job-group>ExampleGroup</job-group>
                <!-- Se dispara cada 10 segundos -->
                <cron-expression>0/10 * * * * ?</cron-expression>
            </cron>
        </trigger>
    </job>
</quartz>



Este XML indica que debe ejecutarse la tarea definida en la clase QuartzExampleJob, con un parámetro de configuración de clave nombre y valor Javier. La tarea se ejecutará según el calendario definido en el disparador de tipo cron QuartzExampleTrigger (cada 10 segundos).
5. Las clases que contienen las tareas deben implementar la interfaz org.quartz.Job, que sólo contiene el método execute. Éste es nuestro ejemplo, que lanza un mensaje en la consola, apoyado en el parámetro que se le pasa en el XML (pueden ser múltiples parámetros, mientras su clave sea distinta).

package com.javisjava.quartz.jobs;

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

public class QuartzExampleJob implements Job {

    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Hola "
            + context.getJobDetail().getJobDataMap().getString("nombre"));
    }

}
6. Al arrancar la aplicación web, aparecerá “Hola Javier” en la consola del contenedor cada 10 segundos.
Os dejo un WAR totalmente funcional que incluye el código para poder modificar la tarea fácilmente.

Como os he dicho, Quartz se puede integrar también en la configuración de Spring, que es el modo recomendado si estáis usando este framework. Hay una sección de la referencia de Spring que cubre cómo hacerlo.

Para profundizar en el manejo de Quartz, acudid a la documentación, o al libro monográfico sobre este framework que ha escrito Chuck Cavaness.

*Edición: tras el comentario de Paul donde pregunta cómo actualizar el trigger una vez iniciado el proceso, he subido proyecto Eclipse muy sencillo donde se muestra la forma de hacerlo.

4 comentarios:

Unknown dijo...

Gracias por el post, me resultó útil la información.

Sin embargo, es imposible leer los bloques de código a simple vista (la sección de quartz_jobs.xml quedó como texto plano, tuve que mirar el código de la página para extraerlo). Podrás editar la entrada para enmarcar estos bloques como código? (por ejemplo con la etiqueta "pre").

camus dijo...
Este comentario ha sido eliminado por el autor.
camus dijo...

Hola, intente realizar el ejemplo pero me tope con algunos problemitas para q funcionara =( pero pues buscando en el Dios google pude allar el problema y ahora ya funciona =). El detalle fue: Aparte de los jars de quartz necesita otros mas para funcionar asi como otros detalles en el xml. Intente publicar la definición de este xml pero no se pudo =(.

Salu2

Unknown dijo...

Hola Camus, me puedes enviar copia del archivo xml al correo anpatri26@gmail.com. Gracias.