Manuale di cron4j 2.2
Questa pagina è disponibile anche in inglese e in serbo-croato
Indice
- Per cominciare
- Scheduling pattern
- Come schedulare, deschedulare e rischedulare un task
- Come schedulare un processo di sistema
- Come schedulare dei processi da un file
- Come preparare un task
- Come preparare un task collector
- Come preparare uno scheduler listener
- Esecutori
- Lancio manuale di un task
- Cambiare il fuso orario dello scheduler
- Daemon thread
- Predictor
- 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:
- Si crea un'istanza di Scheduler.
- 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.
- Si avvia lo scheduler.
- 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:
- È possibile schedulare quanti task si desidera.
- È possibile schedulare nuovi task in qualsiasi momento, anche dopo che lo scheduler è stato avviato.
- È possibile modificare lo scheduling pattern associato ad un task schedulato in precedenza, anche dopo che lo scheduler è stato avviato (reschedulazione).
- È possibile eliminare una schedulazione fatta in precedenza, anche dopo che lo scheduler è stato avviato (deschedulazione).
- È possibile avviare ed arrestare lo scheduler quante volte si desidera.
- È possibile schedulare dei processi descritti in un file tipo /etc/crontab di UNIX.
- È possibile schedulare da qualsiasi tipo di sorgente, ad esempio da un database o da un file XML.
- È possibile agganciare dei listener allo scheduler e ricevere eventi relativi ai task eseguiti.
- È possibile avere il controllo dei task in esecuzione, ad esempio per metterli in pausa, interromperli o per avere informazioni sul loro stato.
- È possibile comandare il lancio immediato di un task, senza ricorrere allo scheduling pattern.
- È possibile cambiare il Time Zone di riferimento dello scheduler.
- È possibile validare gli scheduling pattern prima di passarli allo scheduler.
- È possibile sapere le date in cui un certo scheduling pattern sarà soddisfatto.
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:
- 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. - 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. - 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. - 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". - 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.
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).
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".
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 task può comunicare con il suo esecutore, notificando il suo stato attraverso un messaggio di testo. Questa funzionalità viene chiamata status tracking. Per supportare lo status tracking bisogna ridefinire il metodo supportsStatusTracking(), che deve restituire il valore true. Fatto ciò, all'interno del metodo execute(TaskExecutionContext) è possibile richiamare quante volte si desidera il metodo setStatusMessage(String). Il messaggio di stato consegnato al contesto di esecuzione sarà propagato al suo esecutore. Attraverso l'esecutore, il messaggio di stato può essere intercettato da un oggetto esterno (vedi paragrafo "Esecutori").
-
Un task può comunicare con il suo esecutore, notificando il livello di completamento dell'operazione raggiunto. Questa funzionalità viene chiamata completeness tracking. Per supportare il completeness tracking bisogna ridefinire il metodo supportsCompletenessTracking(), che deve restituire il valore true. Fatto ciò, all'interno del metodo execute(TaskExecutionContext) è possibile richiamare quante volte si desidera il metodo setCompleteness(double), fornendo un valore reale compreso tra 0 ed 1 (estremi compresi). Il valore consegnato al contesto di esecuzione sarà propagato al suo esecutore. Attraverso l'esecutore, il livello può essere intercettato da un oggetto esterno (vedi paragrafo "Esecutori").
-
L'esecuzione di un task, opzionalmente, può essere messa in pausa. Per supportare questa caratteristica è necessario ridefinire il metodo canBePaused(), che deve restituire il valore true. Fatto ciò, all'interno del metodo execute(TaskExecutionContext) è necessario richiamare periodicamente il metodo del contesto pauseIfRequested(). Questo metodo mette in pausa l'esecuzione se un agente esterno lo ha richiesto. Il metodo ritornerà il controllo al codice chiamante solo dopo che un agente esterno ha comando la ripresa (o l'interruzione definitiva) dell'esecuzione (vedi paragrafo "Esecutori").
-
L'esecuzione di un task, opzionalmente, può essere interrotta. Per supportare questa caratteristica è necessario ridefinire il metodo canBeStopped(), che deve restituire il valore true. Fatto ciò, all'interno del metodo execute(TaskExecutionContext) è necessario richiamare periodicamente il metodo del contesto isStopped(). Questo metodo restituisce true se un agente esterno ha comandato l'interruzione dell'esecuzione. A questo punto il codice ha il compito di reagire, provvedendo ad un elegante ma veloce arresto delle operazioni in corso (vedi paragrafo "Esecutori").
-
Attraverso il contesto, il task può recuperare un riferimento al proprio scheduler, con il metodo getScheduler().
-
Attraverso il contesto, il task può recuperare un riferimento al proprio esecutore, con il metodo getTaskExecutor().
Un oggetto Task può essere schedulato, lanciato immediatamente o restituito da un task collector.
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.
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:
-
taskLaunching(TaskExecutor)
Questo metodo viene chiamato ogni volta che l'esecuzione di un task ha inizio. -
taskSucceeded(TaskExecutor)
Questo metodo viene chiamato ogni volta che l'esecuzione di un task è terminata con successo. -
taskFailed(TaskExecutor, Throwable)
Questo metodo viene chiamato ogni volta che l'esecuzione di un task è fallita a causa di un'eccezione non gestita.
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.
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:
executionPausing(TaskExecutor)
Questo metodo viene richiamato quando all'esecutore viene richiesto di mettere in pausa l'esecuzione corrente. Il parametro fornito rappresenta l'esecutore interessato dall'evento.-
executionResuming(TaskExecutor)
Questo metodo viene richiamato quando all'esecutore viene richiesto di riprendere l'esecuzione corrente, dopo che questa è stata messa in pausa. Il parametro fornito rappresenta l'esecutore interessato dall'evento. -
executionStopping(TaskExecutor)
Questo metodo viene richiamato quando all'esecutore viene richiesto di interrompere l'esecuzione corrente. Il parametro fornito rappresenta l'esecutore interessato dall'evento. -
executionTerminated(TaskExecutor, Throwable)
Questo metodo viene richiamato quando l'esecutore ha completato l'esecuzione del task. Il primo parametro fornito rappresenta l'esecutore interessato dall'evento. Il secondo parametro, di tipo java.lang.Throwable, è null nel caso in cui l'esecuzione è terminata regolarmente, mentre è valorizzato con l'eccezione riscontrata nel caso in cui l'esecuzione sia terminata anticipatamente a causa di un errore. -
statusMessageChanged(TaskExecutor, String)
Questo metodo viene richiamato ogni volta che il messaggio di stato dell'esecuzione viene modificato dal task. Il primo parametro fornito rappresenta l'esecutore interessato dall'evento. Il secondo parametro, di tipo java.lang.String, è il nuovo messaggio di stato propagato dal task. -
completenessValueChanged(TaskExecutor, double)
Questo metodo viene richiamato ogni volta che il livello di completamento dell'esecuzione viene aggiornato dal task. Il primo parametro fornito rappresenta l'esecutore interessato dall'evento. Il secondo parametro, di tipo double, è il nuovo valore propagato dal task, sempre e comunque compreso tra 0 ed 1.
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").
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:
- Tempo di sistema: 10:00
- Fuso orario del sistema: GMT+1
- Fuso orario dello scheduler: GMT+3
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).
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().
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()); }
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]
- scheduling-pattern è un pattern di schedulazione valido, in accordo con la definizione di pattern fornita dalla classe it.sauronsoftware.cron4j.SchedulingPattern.
- options è una lista di informazioni aggiuntive opzionali usate da cron4j per allestire l'ambiente di esecuzione del processo. Più basso c'è una descrizione dettagliata delle opzioni supportate.
- command è un comando di sistema valido, ad esempio la chiamata ad un eseguibile.
- args è una lista opzionale di argomenti da fornire al comando.
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:
- \" - doppi apici (quotation mark)
- \\ - controslash (back slash)
- \/ - slash
- \b - cancellazione (back space)
- \f - form feed
- \n - nuova linea (new line)
- \r - ritorno a capo (carriage return)
- \t - tabulatore (horizontal tab)
- \uquattro-cifre-esadecimali - il carattere il cui indice Unicode è quello espresso mediante le quattro cifre esadecimali utilizzate
La parte options è una collezione di uno o più elementi scelti fra i seguenti:
- IN:file-path - Redirige il canale di standard input del comando sul file al percorso specificato.
- OUT:file-path - Redirige il canale di standard output del comando sul file al percorso specificato.
- ERR:file-path - Redirige il canale di standard error del comando sul file al percorso specificato.
- ENV:name=value - Definisce una variabile di ambiente valida durante l'esecuzione del comando.
- DIR:directory-path - Imposta la directory di lavoro del comando. Questa funzionalità non è supportata se la JVM che esegue lo scheduler è antecedente alla versione 1.3.
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