Истеризация streamов в управляемом компоненте JSF для запланированных задач с использованием таймера

Я хотел бы знать, нормально ли использовать Timer внутри приложений с включенными областями.

Например, скажем, что я хочу создать задачу таймера, которая отправляет каждую электронную почту каждому зарегистрированному члену один раз в день. Я пытаюсь использовать как можно больше JSF, и я хотел бы знать, приемлемо ли это (это кажется немного странным, я знаю).

До сих пор я использовал все вышеперечисленное в ServletContextListener . (Я не хочу использовать какой-либо сервер приложений или работу cron, и я хочу сохранить вышеуказанные вещи в своем веб-приложении.)

Есть ли способ сделать JSF умным, или я должен придерживаться старого шаблона?

Введение

Что касается нереста streamа из управляемого компонента JSF, было бы разумно только, если вы хотите иметь возможность ссылаться на него в своих представлениях на #{managedBeanName} или в других управляемых компонентах @ManagedProperty("#{managedBeanName}") , Вы должны только убедиться, что вы реализуете @PreDestroy чтобы гарантировать, что все эти streamи будут закрыты всякий раз, когда произойдет остановка webapp, например, как вы делали бы в режиме contextDestroyed() для ServletContextListener (да, вы сделали?). См. Также Безопасно ли запустить новый stream в управляемом компоненте JSF?

Никогда не используйте java.util.Timer в Java EE

Что касается использования java.util.Timer в управляемом компоненте JSF, вы абсолютно не должны использовать старомодный Timer , но современный ScheduledExecutorService . Timer имеет следующие основные проблемы, которые делают его непригодным для использования в веб-приложении Java EE с длительным сроком действия (цитируется в Java Concurrency in Practice ):

  • Timer чувствителен к изменениям в системных часах, ScheduledExecutorService – нет.
  • Timer имеет только один stream выполнения, поэтому длительная задача может задержать другие задачи. ScheduledExecutorService может быть настроен с любым количеством streamов.
  • Любые исключения во время выполнения, заброшенные в TimerTask убивают один stream, что делает Timer мертвым, то есть запланированные задачи больше не будут выполняться. ScheduledThreadExecutor не только улавливает исключения во время выполнения, но позволяет вам обрабатывать их, если хотите. Задача, которая выбрала исключение, будет отменена, но другие задачи будут продолжать выполняться.

Помимо цитат из книги, я могу придумать больше недостатков:

  • Если вы забыли явно cancel() Timer , то он продолжает работать после undeployment. Поэтому после повторного развертывания создается новый stream, снова выполняющий ту же работу. Etcetera. К настоящему времени он стал «огнем и забыть», и вы больше не можете его программно отменить. В основном вам нужно отключить и перезагрузить весь сервер, чтобы очистить предыдущие streamи.

  • Если stream Timer не отмечен как stream демона, он блокирует undeployment сервера webapp и завершение работы сервера. Вам в основном нужно будет сильно убить сервер. Основным недостатком является то, что webapp не сможет выполнять изящную очистку с помощью contextDestroyed() и @PreDestroy .

Доступен EJB? Использовать @Schedule

Если вы нацелились на Java EE 6 или новее (например, JBoss AS, GlassFish, TomEE и т. Д., И, следовательно, не являетесь контейнером JSP / Servlet, например Tomcat), то вместо этого используйте @Singleton EJB с использованием метода @Schedule . Таким образом, контейнер будет беспокоиться о пуле и уничтожении streamов через ScheduledExecutorService . Все, что вам нужно, это следующий 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. } } 

Это, если необходимо, доступно в управляемых компонентах @EJB :

 @EJB private BackgroundJobManager backgroundJobManager; 

EJB недоступен? Использовать ScheduledExecutorService

Без EJB вам нужно будет вручную работать с ScheduledExecutorService . Реализация управляемой бина реализации приложения будет выглядеть примерно так:

 @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(); } } 

где SomeDailyJob выглядит так:

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

Если вам вообще не нужно ссылаться на это в представлении или на других управляемых компонентах, лучше использовать ServletContextListener чтобы он не был отсоединен от 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(); } } 
  • VBA Macro On Timer для запуска кода с каждым заданным количеством секунд, то есть 120 секунд
  • Давайте будем гением компьютера.