Creazione di thread in un bean gestito JSF per attività pianificate utilizzando un timer

Vorrei sapere se è ansible usare Timer all’interno dei bean con scope dell’applicazione.

Esempio, diciamo che voglio creare un’attività timer che manda un gruppo di email a ogni membro registrato una volta al giorno. Sto cercando di usare il maggior numero ansible di JSF e vorrei sapere se questo è accettabile (è un po ‘strano, lo so).

Fino ad ora ho usato tutto quanto sopra all’interno di un ServletContextListener . (Non voglio utilizzare alcun server di applicazioni o cron job e voglio mantenere le cose di cui sopra all’interno della mia app web.)

C’è un modo JSF intelligente per farlo o dovrei seguire il vecchio modello?

introduzione

Per quanto riguarda la generazione di un thread all’interno di un bean gestito JSF, avrebbe senso solo se si desidera poterlo fare riferimento nelle proprie viste con #{managedBeanName} o in altri bean gestiti da @ManagedProperty("#{managedBeanName}") . Devi solo assicurarti di implementare @PreDestroy per assicurarti che tutti quei thread vengano chiusi ogni volta che la webapp sta per spegnersi, come faresti nel metodo contextDestroyed() di ServletContextListener (sì, hai fatto?). Vedi anche È sicuro iniziare un nuovo thread in un bean gestito JSF?

Non utilizzare mai java.util.Timer in Java EE

Per quanto riguarda l’utilizzo di java.util.Timer in un bean gestito JSF, non si deve assolutamente utilizzare il vecchio Timer , ma il moderno ScheduledExecutorService . Il Timer presenta i seguenti problemi principali che lo rendono inadatto per l’uso in un’applicazione web Java EE di lunga durata (citata da Java Concurrency in Practice ):

  • Timer è sensibile alle modifiche dell’orologio di sistema, ScheduledExecutorService no.
  • Timer ha un solo thread di esecuzione, quindi un’attività a esecuzione prolungata può ritardare altre attività. ScheduledExecutorService può essere configurato con qualsiasi numero di thread.
  • Qualsiasi eccezione di runtime generata in un TimerTask uccide quel thread, rendendo quindi il Timer morto, ovvero le attività pianificate non verranno più eseguite. ScheduledThreadExecutor non rileva solo eccezioni di runtime, ma consente di gestirle se lo si desidera. L’attività che ha generato un’eccezione verrà annullata, ma altre attività continueranno a essere eseguite.

Oltre alle citazioni di libri, posso pensare a più svantaggi:

  • Se si dimentica di cancel() esplicitamente cancel() il Timer , continua a essere in esecuzione dopo l’ cancel() della distribuzione. Quindi, dopo aver ridistribuito, viene creato un nuovo thread, facendo di nuovo lo stesso lavoro. Eccetera. È diventato un “fuoco e dimentica” ormai e non puoi più cancellarlo programmaticamente. Avresti praticamente bisogno di spegnere e riavviare l’intero server per cancellare i thread precedenti.

  • Se il thread Timer non è contrassegnato come thread daemon, bloccherà l’annullamento della distribuzione del webapp e l’arresto del server. Avresti praticamente bisogno di uccidere il server. Il principale svantaggio è che la webapp non sarà in grado di eseguire una pulizia contextDestroyed() @PreDestroy metodi contextDestroyed() e @PreDestroy .

EJB disponibile? Usa @Schedule

Se si targetizza Java EE 6 o successivo (ad es. JBoss AS, GlassFish, TomEE, ecc. E quindi non un contenitore JSP / Servlet barebone come Tomcat), utilizzare invece un bean @Singleton con un metodo @Schedule . In questo modo il contenitore si preoccuperà di raggruppare e distruggere i thread tramite ScheduledExecutorService . Tutto ciò di cui hai bisogno è quindi il seguente EJB:

 @Singleton public class BackgroundJobManager { @Schedule(hour="0", minute="0", second="0", persistent=false) public void someDailyJob() { // Do your job here which should run every start of day. } @Schedule(hour="*/1", minute="0", second="0", persistent=false) public void someHourlyJob() { // Do your job here which should run every hour of day. } @Schedule(hour="*", minute="*/15", second="0", persistent=false) public void someQuarterlyJob() { // Do your job here which should run every 15 minute of hour. } } 

Questo è, se necessario, disponibile nei bean gestiti da @EJB :

 @EJB private BackgroundJobManager backgroundJobManager; 

EJB non disponibile? Utilizzare ScheduledExecutorService

Senza EJB, avresti bisogno di lavorare manualmente con ScheduledExecutorService . L’implementazione del bean gestito con scope dell’applicazione sarà simile a questa:

 @ManagedBean(eager=true) @ApplicationScoped public class BackgroundJobManager { private ScheduledExecutorService scheduler; @PostConstruct public void init() { scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS); } @PreDestroy public void destroy() { scheduler.shutdownNow(); } } 

dove il SomeDailyJob assomiglia a questo:

 public class SomeDailyJob implements Runnable { @Override public void run() { // Do your job here. } } 

Se non è necessario fare riferimento nella vista o in altri bean gestiti, utilizzare semplicemente ServletContextListener per mantenerlo disgiunto da JSF.

 @WebListener public class BackgroundJobManager implements ServletContextListener { private ScheduledExecutorService scheduler; @Override public void contextInitialized(ServletContextEvent event) { scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS); } @Override public void contextDestroyed(ServletContextEvent event) { scheduler.shutdownNow(); } }