Sauron Software Projects Repository
cron4j
Presentazione Documentazione Download

Manuale di cron4j 2.2

Questa pagina è disponibile anche in inglese e in serbo-croato

Indice

  1. Per cominciare
  2. Scheduling pattern
  3. Come schedulare, deschedulare e rischedulare un task
  4. Come schedulare un processo di sistema
  5. Come schedulare dei processi da un file
  6. Come preparare un task
  7. Come preparare un task collector
  8. Come preparare uno scheduler listener
  9. Esecutori
  10. Lancio manuale di un task
  11. Cambiare il fuso orario dello scheduler
  12. Daemon thread
  13. Predictor
  14. Cron parser

1. Per cominciare

L'entità principale di cron4j è lo scheduler . Con un'istanza della classe it.sauronsoftware.cron4j.Scheduler è possibile eseguire dei task in dei momenti prefissati, lungo l'arco dell'anno. Uno scheduler può eseguire un task una volta al minuto, una volta ogni cinque minuti, Venerdì alle 10:00 in punto, il 16 di Febbraio alle 12.30 ma solo se cade di Sabato, e così via.

L'utilizzo dello scheduler di cron4j è un'operazione che si esegue in quattro passi:

  1. Si crea un'istanza di Scheduler.
  2. Si schedulano le azioni desiderate. Per schedulare un'azione è necessario comunicare allo scheduler cosa deve fare e quando deve farlo. Il cosa può essere specificato servendosi di un'istanza di java.lang.Runnable o di it.sauronsoftware.cron4j.Task. Il quando può essere specificato servendosi di uno scheduling pattern, che può essere rappresentato con una semplice stringa o con un'istanza della classe it.sauronsoftware.cron4j.SchedulingPattern.
  3. Si avvia lo scheduler.
  4. Si arresta lo scheduler, quando non è più necessario.

Si prenda in considerazione il seguente semplice esempio:

import it.sauronsoftware.cron4j.Scheduler;

public class Quickstart {

	public static void main(String[] args) {
		// Crea l'istanza dello scheduler.
		Scheduler s = new Scheduler();
		// Schedula un task, che sarà eseguito ogni minuto.
		s.schedule("* * * * *", new Runnable() {
			public void run() {
				System.out.println("Un altro minuto è trascorso...");
			}
		});
		// Avvia lo scheduler.
		s.start();
		// Lascia in esecuzione per dieci minuti.
		try {
			Thread.sleep(1000L * 60L * 10L);
		} catch (InterruptedException e) {
			;
		}
		// Arresta lo scheduler.
		s.stop();
	}

}

L'esempio resta in esecuzione per circa dieci minuti. Allo scoccare di ogni nuovo minuto, secondo l'orologio del sistema ospita, stamperà il triste (ma vero) messaggio "Un altro minuto è trascorso...".

Alcuni altri concetti chiave:

Torna all'indice

2. Scheduling pattern

Un pattern di schedulazione "a la UNIX" è rappresentato con una stringa divisa in cinque parti, ognuna separata dalla successiva attraverso una spaziatura. I cinque campi sono, rispettivamente:

  1. Sotto-pattern per i minuti.
    Attraverso questo pattern si esprime in quali minuti si desidera avviare il task. I valori ammessi vanno da 0 a 59.
  2. Sotto-pattern per le ore.
    Attraverso questo pattern si esprime in quali ore si desidera avviare il task. I valori ammessi vanno da 0 a 23.
  3. Sotto-pattern per il giorno del mese.
    Attraverso questo pattern si esprime in quali giorni del mese si desidera avviare il task. I valori ammessi vanno da 1 a 31. Lo speciale valore "L" può essere utilizzato per riconoscere automaticamente l'ultimo giorno del mese.
  4. Sotto-pattern per il mese.
    Attraverso questo pattern si esprime in quali mesi si desidera avviare il task. I valori ammessi vanno da 1 (gennaio) a 12 (dicembre) , oppure è possibile usare le stringhe-equivalenti "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov" e "dec".
  5. Sotto-pattern per il giorno della settimana.
    Attraverso questo pattern si esprime in quali giorni della settimana si desidera avviare il task. I valori ammessi vanno da 0 (domenica) a 6 (sabato), oppure è possibile usare le stringhe-equivalenti: "sun", "mon", "tue", "wed", "thu", "fri" e "sat".

Se non si intende imporre una restrizione precisa su uno dei campi si può usare il carattere jolly asterisco, che sta a significare, a seconda del contesto, tutti i minuti, tutte le ore, tutti i giorni del mese ecc.

Una volta che lo scheduler è stato avviato, un task sarà eseguito quando le cinque parti del suo pattern di schedulazione risulteranno contemporaneamente soddisfatte.

I pattern di schedulazione possono essere rappresentati con le istanze della classe it.sauronsoftware.cron4j.SchedulingPattern. Pattern di schedulazione non validi causano il lancio di eccezioni del tipo it.sauronsoftware.cron4j.InvalidPatternException. La classe SchedulingPattern offre inoltre il metodo statico validate(String), che può essere utilizzato per validare una stringa prima di utilizzarla come un pattern di schedulazione.

Alcuni esempi:

5 * * * *
Questo pattern avvia il task cui è collegato una volta all'ora, esattamente ogni volta che scatta il quinto minuto di un'ora (alle 00:05, alle 01:05, alle 02:05 e così via).

* * * * *
Questo pattern avvia il task cui è collegato allo scattare di ogni minuto.

0 12 * * Mon
Questo pattern avvia il task cui è collegato allo scattare delle 12:00 di ogni lunedì.

0 12 16 * Mon
Questo pattern avvia il task cui è collegato allo scattare delle 12:00 del 16 del mese, ma solo se è lunedì.

Su un singolo sotto-pattern, secondo esigenza, è possibile esprimere più condizioni di avvio, separandole con una virgola. Ad esempio:

59 11 * * 1,2,3,4,5
Questo pattern avvia il task cui è collegato alle 11:59 di ogni lunedì, martedì, mercoledì, giovedì e venerdì.

E' inoltre possibile esprimere degli intervalli, con il carattere meno:

59 11 * * 1-5
Questo pattern è, nel significato, identico al precedente.

Il carattere slash può essere usato per identificare dei valori all'interno di un range. Si usa sia nella forma */c sia in quella a-b/c. Il sottopattern così espresso viene soddisfatto ogni c valori che sono nel range 0,valore-massimo oppure a-b.

*/5 * * * *
Questo pattern avvia il task collegato ogni 5 minuti (0:00, 0:05, 0:10, 0:15 e così via).

3-18/5 * * * *
Questo pattern avvia il task collegato ogni 5 minuti a partire dal terzo di ogni ora, fino al diciottesimo (0:03, 0:08, 0:13, 0:18, 1:03, 1:08 e così via).

*/15 9-17 * * *
Questo pattern avvia il task cui è collegato ogni 15 minuti nelle ore che vanno dalle 9 alle 17. In pratica il task sarà avviato alle 09:00, alle 09:15, alle 09:30, alle 09:45, alle 10:00 e così via, fino ad arrivare alle 17:45.

* 12 10-16/2 * *
Questo pattern avvia il task cui è collegato ogni minuto delle ore 12, ogni due giorni nell'intervallo 10-16.

* 12 10,12,14,16 * *
Questo pattern è identico al precedente ma invece dello slash scrive la condizione per esteso.

Tutte le regole illustrate sinora per i sotto-pattern possono essere combinate assieme per esprimere condizioni di maggiore complessità. Alcuni esempi:

* 12 1-15,17,20-25 * *
Esegue il task ogni minuto delle ore 12, purché il giorno del mese sia o tra il primo ed il 15, o il 17, o tra il 20 ed il 25.

Infine più pattern possono essere concatenati in un pattern composto, usando il simbolo pipe come separatore:

0 5 * * *|8 10 * * *|22 17 * * *
Esegue il task ogni giorno di ogni mese alle ore 05:00, alle ore 10:08 e alle ore 17:22.

Torna all'indice

3. Come schedulare, deschedulare e rischedulare un task

La maniera più semplice per costruire un task è implementare la ben nota interfaccia java.lang.Runnable. Quando il task è pronto può essere schedulato con il metodo it.sauronsoftware.cron4j.Scheduler.schedule(String, Runnable). Il metodo lancia una it.sauronsoftware.cron4j.InvalidPatternException se la stringa utilizzata come scheduling pattern è formalmente non valida (vedi paragrafo precedente).

Un'altra maniera per allestire un task è estendo la classe astratta it.sauronsoftware.cron4j.Task, che permette un controllo più granulare sulle interazioni tra lo scheduler ed il task stesso. Questo aspetto viene approfondito nel paragrafo "Come preparare un task". Le istanze di Task possono essere schedulate con i metodi schedule(String, Task) e schedule(SchedulingPattern, Task).

I metodi di schedulazione restituiscono sempre un ID che serve per riconoscere e recuperare l'operazione schedulata. Questo ID può essere successivamente utilizzato per rischedulare l'operazione (cioè per cambiare il suo scheduling pattern), con i metodi reschedule(String, String) e reschedule(String, SchedulingPattern), oppure l'ID può essere usato per deschedulare il task (annulare la sua schedulazione) con il metodo deschedule(String).

Il medesimo ID può essere utilizzato anche per recuperare il pattern di schedulazione associato al task, con il metodo getSchedulingPattern(String), ed anche per recuperare il task stesso, con il metodo getTask(String).

Torna all'indice

4. Come schedulare un processo di sistema

I processi di sistema possono essere schedulati servendosi della classe wrapper ProcessTask:

ProcessTask task = new ProcessTask("C:\\Windows\\System32\\notepad.exe");
Scheduler scheduler = new Scheduler();
scheduler.schedule("* * * * *", task);
scheduler.start();
// ... 

Argomenti al processo da eseguire possono essere forniti servendosi di un array di stringhe:

String[] command = { "C:\\Windows\\System32\\notepad.exe", "C:\\File.txt" };
ProcessTask task = new ProcessTask(command);
// ...

Variabili d'ambiente valide per il lancio del processo possono essere fornite servendosi di un secondo array di stringhe. Ciascuna variabile deve essere espressa nella forma NOME=VALORE:

String[] command = { "C:\\tomcat\\bin\\catalina.bat", "start" };
String[] envs = { "CATALINA_HOME=C:\\tomcat", "JAVA_HOME=C:\\jdks\\jdk5" };
ProcessTask task = new ProcessTask(command, envs);
// ...

La directory di lavoro del processo può essere impostata come terzo argomento del costrutore:

String[] command = { "C:\\tomcat\\bin\\catalina.bat", "start" };
String[] envs = { "CATALINA_HOME=C:\\tomcat", "JAVA_HOME=C:\\jdks\\jdk5" };
File directory = "C:\\MiaDirectory";
ProcessTask task = new ProcessTask(command, envs, directory);
// ...

Se nessuna variabile di ambiente deve essere specificata ma si vuole usare il costruttore a tre argomenti per impostare la directory di lavoro, l'argomento envs può essere impostato su null:

ProcessTask task = new ProcessTask(command, null, directory);

Quando envs è null il processo eredita le varibiabili di ambiente della JVM che sta eseguendo il codice Java.

Le variabili d'ambiente e la directory di lavoro possono essere impostati anche successivamente alla creazione del task, servendosi dei metodi setEnvs(String[]) e setDirectory(java.io.File).

I canali di standard output e standard error del processo possono essere redirezionati verso dei file con i metodi setStdoutFile(java.io.File) e setStderrFile(java.io.File):

ProcessTask task = new ProcessTask(command, envs, directory);
task.setStdoutFile(new File("out.txt"));
task.setStderrFile(new File("err.txt"));

Lo standard input, in maniera simile, può essere letto da un file esistente, impostandolo con il metodo setStdinFile(java.io.File):

ProcessTask task = new ProcessTask(command, envs, directory);
task.setStdinFile(new File("in.txt"));

5. Come schedulare dei processi da un file

Lo scheduler di cron4j permette di schedulare una serie di processi dichiarati in un file esterno all'applicazione.

Per prima cosa è necessario preparare il file, che è estremamente simile a quello utilizzato dal crontab di UNIX. Quindi il file va registrato nello scheduler con il metodo scheduleFile(File). Successivamente il file può essere rimosso dallo scheduler con il metodo descheduleFile(File). L'elenco di tutti i file schedulati è disponibile chiamando il metodo getScheduledFiles().

I file schedulati vengono esaminati ed interpretati ogni minuto. Lo scheduler, al termine del parsing ricorrente, lancia tutti quei processi il cui pattern di schedulazione risulta verificato dall'orologio di sistema.

Le regole sintattiche per i file di cron4j sono riportate nel paragrafo "Cron parser".

Torna all'indice

6. Come preparare un task

Un oggetto di tipo java.lang.Runnable costituisce la più semplice forma di task, ma per avere un controllo più dettagliato di quello che accade durante l'esecuzione di una routine è necessario estendere la classe it.sauronsoftware.cron4j.Task. Dal punto di vista dello sviluppatore, implementare Runnable o estendere Task è quasi la stessa cosa: mentre la prima richiede l'implementazione del metodo run(), Task richiede invece l'implementazione del metodo execute(TaskExecutionContext). Questo metodo fornisce sempre un oggetto di tipo it.sauronsoftware.cron4j.TaskExecutionContext, che Runnable.run() non fornisce. Il contesto ricevuto in argomento può essere impiegato nelle seguenti maniere:

Un oggetto Task può essere schedulato, lanciato immediatamente o restituito da un task collector.

Torna all'indice

7. Come preparare un task collector

È possibile innestare all'interno dello scheduler di cron4j una sorgente di task personalizzata, sfruttando un task collector.

Lo scheduler di cron4j, infatti, supporta la registrazione di uno o più oggetti di tipo it.sauronsoftware.cron4j.TaskCollector, attraverso il metodo addTaskCollector(TaskCollector). I collector registrati possono essere successivamente recuperato con il metodo getTaskCollectors(). Un collector registrato nello scheduler può essere rimosso servendosi del metodo removeTaskCollector(TaskCollector). I collector possono essere aggiunti, rimossi o recuperati in qualsiasi momento, anche quando lo scheduler è avviato.

Ogni collector registrato viene consultato dallo scheduler una volta al minuto. Lo scheduler richiama il metodo getTasks() del collector. L'implementazione del metodo è tenuta a restituire un oggetto di tipo it.sauronsoftware.cron4j.TaskTable. Una TaskTable è una tabella che associa task e pattern di schedulazione. Lo scheduler scorre le righe della tabella restituita, ed esegue tutti quei task il cui pattern di schedulazione è in accordo con l'orario del sistema.

Un collector personalizzato può essere impiegato per recuperare la lista dei task da eseguire da una sorgente esterna, ad esempio un database o un file XML, i cui contenuti possono essere variati in qualsiasi momento durante l'esecuzione del software.

Torna all'indice

8. Come preparare uno scheduler listener

Implementando l'interfaccia it.sauronsoftware.cron4j.SchedulerListener è possibile realizzare dei listener da registrare su uno scheduler.

L'interfaccia SchedulerListener richiede l'implementazione dei seguenti metodi:

Si veda il paragrafo "Esecutori" per avere maggiori informazioni circa gli esecutori di task.

Gli oggetti SchedulerListener possono essere registrati in uno scheduler attraverso il metodo addSchedulerListener(SchedulerListener). I listener registrati in precedenza possono essere rimossi chiamato il metodo removeSchedulerListener(SchedulerListener). La lista di tutti i listener registrati è messa a disposizione dal metodo getSchedulerListeners().

Gli SchedulerListener possono essere aggiunti, rimossi e consultati in qualsiasi momento, anche quando lo scgheduler è avviato.

Torna all'indice

9. Esecutori

Lo scheduler, quando è attivo, può restituire i propri esecutori. Un esecutore è simile ad un thread. Lo scheduler usa gli esecutori per eseguire i task.

Chiamando il metodo Scheduler.getExecutingTasks() si ottiene la lista degli esecutori correntemente attivi.

L'esecutore associato all'esecuzione di un task viene anche consegnato agli SchedulerListener registrati nello scheduler (si veda il paragrafo "Come preparare uno scheduler listener").

Ogni esecutore, rappresentato da un oggetto it.sauronsoftware.cron4j.TaskExecutor, cura l'esecuzione di un differente task.

Il task eseguito è recuperabile attraverso il metodo getTask().

Lo stato dell'esecutore può essere controllato con il metodo isAlive(), che restituisce true se l'esecutore è correntemente in esecuzione.

Se l'esecutore è attivo, è possibile fermarsi in attesa del completamento del task chiamando il metodo join().

Il metodo supportsStatusTracking() restituisce true se il task in esecuzione supporta lo status tracking. In questo caso è possibile recuperare lo stato dell'esecuzione invocando il metodo getStatusMessage().

Il metodo supportsCompletenessTracking() restituisce true se il task in esecuzione supporta il completeness tracking. In questo caso è possibile recuperare il livello di completamento dell'esecuzione invocando il metodo getCompleteness(). Il metodo restituisce un valore compreso tra 0 (esecuzione appena avviata) ed 1 (esecuzione conmpletata), estremi compresi.

Il metodo canBePaused() restituisce true se il task in esecuzione accetta di poter essere messo in pausa. In questo caso è possibile mettere in pausa l'esecuzione chiamando il metodo pause(). Con il metodo isPaused(), invece, è possibile controllare se l'esecuzione è stata messa in pausa. L'esecuzione può essere ripresa servendosi del metodo resume().

Il metodo canBeStopped() restituisce true se il task in esecuzione accetta di poter essere interrotto. In questo caso è possibile interrompere l'esecuzione chiamando il metodo stop(). Con il metodo isStopped(), invece, è possibile controllare se l'esecuzione è stata interrotta. Una esecuzione interrotta non può essere ripresa.

Il metodo getStartTime() restituisce il timestamp corrispondente al momento in cui l'esecutore è stato avviato, oppure un valore negativo se l'esecutore non è ancora stato avviato.

Il metodo getScheduler() restituisce lo scheduler proprietario dell'esecutore.

Il metodo getGuid() restituisce un ID universalmente univoco associato all'esecutore, sotto forma di stringa.

Gli esecutori offrono inoltre un'interfaccia di programmazione basata sugli eventi, attraverso l'interfaccia it.sauronsoftware.cron4j.TaskExecutorListener. Un TaskExecutorListener può essere registrato su un TaskExecutor chiamando il suo metodo addTaskExecutorListener(TaskExecutorListener). Successivamente il listener può essere rimosso con il metodo removeTaskExecutorListener(TaskExecutorListener). L'elenco dei listener registrati in un esecutore può essere recuperato con il metodo getTaskExecutorListeners().

L'interfaccia TaskExecutorListener richiede i seguenti metodi:

Torna all'indice

10. Lancio manuale di un task

Se los cheduler è attivo, è possibile comandare esplicitamente il lancio immediato di un task, anche se questo non è schedulato o se il suo scheduling pattern non è al momento rispettato. Il metodo necessario è Scheduler.launch(Task), che restituisce l'istanza di TaskExecutor che rappresenta l'esecuzione del task appena lanciato (si veda il paragrafo "Esecutori").

Torna all'indice

11. Cambiare il fuso orario dello scheduler

Lo scheduler, per default, utilizza il fuso orario impostato sul sistema ospite. È però possibile richiedere ad uno scheduler di lavorare secondo un fuso orario (time zone) differente. I metodi per il controllo di questa caratteristica sono Scheduler.setTimeZone(TimeZone) e Scheduler.getTimeZone().

Se si cambia il fuso orario di riferimento, il tempo segnato dall'orologio di sistema sarà automaticamente adattato al fuso di destinazione, prima di essere utilizzato dallo scheduler per verificare i pattern di schedulazione associati ai task registrati.

Si ipotizzi questa situazione:

Lo scheduler, prima di confrontare il tempo di sistema con i pattern registrati, converte l'orario 10:00 da GMT+1 to GMT+3. Significa che 10:00 diventa 12:00 (quando nel fuso GMT+1 sono le 10:00, nel fuso GMT+3 sono le 12:00). A questo punto saranno eseguiti quei task il cui pattern di schedulazione sia soddisfatto dall'orario 12:00, e non quelli soddisfatti dall'orario 10:00 (ad esempio 0 12 * * * sarà eseguito, mentre 0 10 * * * no).

Torna all'indice

12. Thread demoni

La Java Virtual Machine termina quando tutti i thread rimasti in esecuzione sono thread demoni. Se necessario, lo scheduler di cron4j può essere configurato affinché qualsiasi thread da lui generato sia marcato come thread demone. La caratteristica viene controllata con il metodo Scheduler.setDaemon(boolean). il metodo deve essere richiamato prima che lo scheduler venga avviato. Il valore predefinito è false (se non si chiamata setDaemon(true), quindi, lo scheduler genera tutti thread che non sono demoni). Per verificare l'impostazione corrente si deve chiamare il metodo Scheduler.isDaemon().

Torna all'indice

13. Predictor

La classe it.sauronsoftware.cron4j.Predictor è in grado di predire il momento in cui un certo pattern di schedulazione sarà soddisfatto.

Si immagini di voler conoscere quando lo scheduler eseguirà le prossime n esecuzioni di un task il cui pattern associato è 0 3 * jan-jun,sep-dec mon-fri. Ecco come fare:

String pattern = "0 3 * jan-jun,sep-dec mon-fri";
Predictor p = new Predictor(pattern);
for (int i = 0; i < n; i++) {
	System.out.println(p.nextMatchingDate());
}

Torna all'indice

14. Cron parser

La classe it.sauronsoftware.cron4j.CronParser può essere usata per interpretare testi simili a quelli utilizzati nel file crontab di UNIX.

Se l'intenzione è schedulare una serie di processi dichiarati in un file tipo il crontab, non c'è bisogno di ricorrere al CronParser: è sufficiente agire con il metodo Scheduler.scheduleFile(File).

Il CronParser può invece essere utilizzato quando il metodo Scheduler.scheduleFile(File) non è abbastanza. Ad esempio, la lista dei processi da eseguire è conservata nelle righe di una tabella di un database, e non nelle righe di un file. Per risolvere il problema è possibile implementare il proprio it.sauronsoftware.cron4j.TaskCollector, sfruttando i servigi della classe CronParser per non dover ogni volta reinventare la ruota.

Con il CronParser è possibile esaminare un intero file, un intero stream oppure una semplice riga di testo alla volta.

Una linea può essere vuota, può contenere un commento o può essere una linea di schedulazione.

Una linea senza caratteri, o una linea con soli caratteri di spaziatura è considerata vuota dal CronParser.

Una linea il cui primo carattere non di spaziatura è un cancelletto (#) viene considerata come un commento.

Sia le linee vuote sia le linee di commento vengono ignorate dal parser.

Le linee di schedulazione, invece, vengono prese in esame.

Una linea di schedulazione, per essere valida, deve rispettare la forma:

scheduling-pattern [options] command [args]

Dopo il pattern di schedulazione, tutti gli altri elementi di ciascuna linea sono separati da caratteri di spaziatura, oppure delimitati con una coppia di doppi apici (").

Gli elementi racchiusi tra doppi apici possono far uso delle seguenti sequenze di escape:

La parte options è una collezione di uno o più elementi scelti fra i seguenti:

Si può schedulare anche l'invocazione di un metodo di una classe Java, purché la classe possa essere caricara dal ClassLoader che carica lo scheduler. Il metodo, nello specifico, deve essere statico e deve accettare come solo argomento un array di stringhe. Un metodo di questo tipo può essere richiamato con una linea del seguente tipo:

scheduling-pattern java:nomeClasse#nomeMetodo [args]

La parte #nomeMetodo può essere omessa: in questo caso sarà ricercato automaticamente il metodo main(String[]).

Si faccia attenziona al fatto che i metodi statici vengono invocati ed eseguiti all'interno della stessa JVM che esegue lo scheduler. Pertanto non è possibile applicare ai task di questo tipo le opzioni IN, OUT, ERR, ENV e DIR, valide invece per i processi.

Le linee di schedulazione non valide vengono scartate senza bloccare l'operazione di parsing, ma un messaggio di errore viene emesso sul canale di standard error dell'applicazione.

Ecco qualche esempio di linee di scheduling valide su un sistema Windows:

0 5 * * * sol.exe
0,30 * * * * OUT:C:\ping.txt ping 10.9.43.55
0,30 4 * * * "OUT:C:\Documents and Settings\Carlo\ping.txt" ping 10.9.43.55
0 3 * * * ENV:JAVA_HOME=C:\jdks\1.4.2_15 DIR:C:\myproject OUT:C:\myproject\build.log C:\myproject\build.bat "Nightly Build"
0 4 * * * java:mypackage.MyClass#startApplication myOption1 myOption2

Torna all'indice

© Sauron Software 2007 - 2012 | This page in English