SERVEI DE PLANIFICACIÓ DE TASQUES









Introducció

Propósit

El Servei de Planificació de Tasques de canigo permet configurar l'execució de tasques de forma diferida en els moments en els que es determini:

  • en qualsevol moment del dia (precisió de milisegons)
  • en alguns dies de la setmana
  • en alguns dies del mes
  • en alguns dies de l'any
  • un número específic de repeticions
  • repetidament fins a una data/instant determinat
  • repetida e indefinidament
  • repetidament en un determinat interval de temps

Context i Escenaris d'Ús

El Servei de Planificació de Tasques es troba dins la Capa de Dades/Integració en el context dels serveis proporcionats per canigo.

Versions i Dependències

Les dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte:
Dependències Servei de Planificació de Tasques

A qui va dirigit

Aquest document va dirigit als següents perfils:

  1. Programador. Per conéixer l'ús del servei
  2. Arquitecte. Per conéixer quins són els components i la configuració del servei
  3. Administrador. Per conéixer cóm configurar el servei en cadascun dels entorns en cas de necessitat

Documents i Fonts de Referència

[1] Quartz http://www.opensymphony.com/quartz

Descripció Detallada

Arquitectura i Components

El Servei de Planificació de Tasques de canigo ofereix interfícies d'ús que s'abstreuen de la implementació escollida per millor mantenibilitat en futures versions i futures implementacions alternatives.

En l'actualitat, canigo proporciona una implementació basada en Quartz (projecte open source desenvolupat per PartNET).

Els components podem classificar-los en:

  1. Interfícies i Components Genérics. Interfícies del servei i components d'ús general amb independència de la implementació escollida.
  2. Implementació de les interfícies basada en Quartz
    Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls:

JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-scheduler/apidocs/index.html
Codi Font:  http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-scheduler/xref/index.html

Instal- lació i Configuració

Instal- lació

La instal- lació del servei requereix de la utilització de la llibreria 'canigo-services-scheduler' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'.

Configuració

Fitxer de configuració: canigo-services-scheduler.xml

Ubicació proposada: <PROJECT_ROOT>/src/main/resources/spring

Configuració

La configuració del Servei de Planificació de tasques implica 3 pasos:

  1. Definir les tasques que volem executar de forma diferida
  2. Definir els triggers que defineixen en quin moment s'executaran les tasques
  3. Definir la factoria per executar les tasques amb els triggers

Definició de les tasques

Per a definir una tasca cal definir 2 parts:

  1. Tasca

La tasca és simplement la referència a la classe que conté el mètode que volem executar de forma diferida.

Exemple:

<!- TASKS DEFINITIONS ->
<bean id="taskWriteLog" class="net.gencat.ctti.canigo.samples.jpetstore.scheduler.TaskWriteLog"/>




La classe tasca és un POJO que no ha d'implementar cap interfície en concret ni extendre cap classe.

  1. Detall de la tasca

Per definir els detalls de la tasca per tal que pugui ser executada de forma diferida podem fer ús de 2 classes:

  1. 'SpringQuartzMethodInvokingJobDetailFactoryBean'. Amb aquesta podrem fer referència al mètode concret a executar. Es permet definir les següents propietats:
Propietat Requerit Descripció
targetObject Referència a la classe que conté el mètode a executar de forma diferida
targetMethod Referència al mètode de la classe que s'executarà
arguments No Llista d'arguments a passar al mètode. Podem fer ús dins la llista de valors directes (amb <value>) o referències (amb <ref bean>). Aquesta llista ha de ser creada en el mateix ordre que la llista d'arguments definida al mètode.

Exemple:

<property name="arguments">
<list>
<value>5</value>
<ref bean="logService"/>
</list>
</property>
concurrent No Indicar si es poden executar de forma concurrent vàries instàncies de la tasca

Recomanació: Usar 'false'

Per defecte: true

Exemple:

<bean id="taskWriteDetail" class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.
SpringQuartzMethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="taskWriteLog"/>
    <property name="targetMethod" value="writeLog"/>
    <property name="concurrent" value="false"/>
</bean>





  1. 'SpringQuartzJobDetailBean'. En aquest cas la classe haurà d'extendre la classe 'SpringQuartzJobBean'.
Propietat Requerit Descripció
jobClass Referència a la classe que extén la classe 'SpringQuartzJobBean'
jobDataAsMap Map de dades que passarem a la tasca. Cadascun dels keys ha de tenir un 'set' corresponent a la classe

Exemple:

<bean name="exampleJob" class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.
SpringQuartzJobDetailBean">
    <property name="jobClass" value="example.ExampleJob"/>
    <property name="jobDataAsMap">
        <map>
            <entry key="timeout" value="5"/>
        </map>
    </property>
</bean>




En aquest cas, la tasca seria definida així:

package example;

// import section
...

public class ExampleJob extends SpringQuartzJobBean {
    private int timeout;

    /**
    * Setter called after the ExampleJob is instantiated
    * with the value from the JobDetailBean (5)
    */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    protected void executeInternal(JobExecutionContext ctx) throws SchedulerServiceException {
        // do the actual work
    }
}




Com veiem, la classe defineix el mètode 'executeInternal' on realitzarà el procediment de la tasca.

Definició dels triggers

Mitjançant els triggers configurarem en quin moment s'ha d'executar la tasca.

canigo permet la utilització de 2 classes:

  1. 'SpringQuartzSimpleTriggerBean'
Propietat Requerit Descripció
jobDetail Referència a la tasca definida en el pas anterior
endTime No Hora de finalització del trigger
startDelay No Temps (en mil- lisegons) que ha de transcórrer abans d'executar-se el primer trigger
startTime Hora d'inicia de la tasca
repeatInterval No Temps (en mil- lisegons) que ha de transcórrer abans d'executar-se el següent trigger

Exemple:

<!-- TRIGGERS DEFINITIONS -->
<bean id="taskWriteTrigger" class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.
SpringQuartzSimpleTriggerBean">
    <!-- see the example of method invoking job above -->
    <property name="jobDetail" ref="taskWriteDetail"/>
    <!-- 10 seconds -->
    <property name="startDelay" value="10000"/>
    <!-- repeat every 20 seconds -->
    <property name="repeatInterval" value="20000"/>
    <property name="concurrent" value="false"/>
</bean>






  1. 'SpringQuartzCronTriggerBean'.
Propietat Requerit Descripció
jobDetail Referència a la tasca definida en el pas anterior
cronExpression Expressió de tipus cron de Unix. Per a més informació consultar http://wiki.opensymphony.com/display/QRTZ1/CronTriggers+Tutorial

Exemple:

<bean id="taskWriteTrigger2" class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.
SpringQuartzCronTriggerBean">
    <!-- see the example of method invoking job above -->
    <property name="jobDetail" ref="taskWriteDetail"/>
    <!-- run every morning at 6 AM -->
    <property name="cronExpression" value="0 0 6 * * ?"/>
    <property name="concurrent" value="false"/>
</bean>





Definició de la factoria dels triggers

Per últim, definirem un bean de la classe ' net.gencat.ctti.canigo.services.scheduler.impl.quartz.SpringQuartzSchedulerFactoryBean' on definirem les propietats:

Propietat Requerit Descripció
triggers Llista de referències als triggers anteriorment definits

Exemple:

<!-- SCHEDULER FACTORY BEAN DEFINITION -->
<bean class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.SpringQuartzSchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="taskWriteTrigger"/>
            <ref bean="taskWriteTrigger2"/>
        </list>
    </property>
</bean>





Utilització del Servei

La instanciació, la preparació i la petició del servei es fa de manera transparent, de tal manera que el servei s'activa en el moment en que el "Scheduler Factory Bean" conté algun Trigger amb alguna tasca associada (tal i com he definit a la configuració).

En el servei, es defineixen a la configuració les tasques (Jobs) i els disparadors (Triggers) que llençaran les tasques. Les tasques, en general no han d'extendre o implementar cap interfície, però sí serà necessari en cas de definir 'SpringQuartzJobDetailBean'.

Per tant, la utilització del servei es realitzar pràcticament en la seva totalitat mitjançant la seva configuració.

Exemples

Com exemple d'utilització del servei de Planificació de Tasques s'inclou un exemple en el que 2 Triggers invoquen un mètode d'una classe Java que genera un log (mitjançant el servei de traces):

En el codi mostrat a baix, la tasca definida no implementa cap classe. Defineix únicament el mètode 'writeLog' on realitza la traça

package net.gencat.ctti.tutorial.web.temp;

import org.apache.log4j.Logger;

public class TaskWriteLog {

    public TaskWriteLog() {
    }

    public void writeLog(LoggingService logService) {
        if (logService!=null) {
            logService.getLog(this.getClass()).debug("Doing log at:" + System.
            currentTimeMillis());
        }

    }
}





Un exemple de la configuració de la seva execució diferida és la següent:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/
spring-beans.dtd?">
<beans>
    <!-- SCHEDULER FACTORY BEAN DEFINITION -->
    <bean class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.
    SpringQuartzSchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="taskWriteTrigger"/>
            </list>
        </property>
    </bean>

    <!-- TASKS DEFINITIONS -->
    <bean id="taskWriteLog" class="net.gencat.ctti.tutorial.web.temp.TaskWriteLog"/>
    <bean id="taskWriteDetail" class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.
    SpringQuartzMethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="taskWriteLog"/>
        <property name="targetMethod" value="writeLog"/>
        <property name="arguments">
            <list>
                <ref bean="logService"/>
            </list>
        </property>
            <property name="concurrent" value="false"/>
    </bean>

    <!-- TRIGGERS DEFINITIONS -->
    <bean id="taskWriteTrigger" class="net.gencat.ctti.canigo.services.scheduler.impl.quartz.
    SpringQuartzSimpleTriggerBean">
        <!-- see the example of method invoking job above -->
        <property name="jobDetail" ref="taskWriteDetail"/>
        <!-- 10 seconds -->
        <property name="startDelay" value="10000"/>
        <!-- repeat every 20 seconds -->
        <property name="repeatInterval" value="20000"/>
    </bean>
</beans>





Nota
En el cas que 2 Triggers utilitzin la mateixa tasca, es podria donar el cas de que abans de que el primer Trigger finalitzi, ja es comenci a executar el segon. Per evitar aquesta situació s'afegirà en la definició de la tasca la propietat _concurrent_ amb el valor "false"