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

Я хотел бы знать, нормально ли это использовать Timer внутри приложения определены области фасоли.

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

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

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

1 ответ

Решение

Вступление

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

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

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

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

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

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

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

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

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

}

Если вам вообще не нужно ссылаться на него в представлении или других управляемых bean-компонентах, лучше просто использовать 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();
    }

}
Другие вопросы по тегам