Spawanie wątków w zarządzanej fasoli JSF dla zaplanowanych zadań za pomocą timera

Chciałbym wiedzieć, czy można używać Timer wewnątrz aplikacji scoped beans.

Przykład, powiedzmy, że chcę utworzyć zadanie timera, które wysyła kilka e-maili do każdego zarejestrowanego użytkownika jeden raz dziennie. Staram się używać jak najwięcej JSF i chciałbym wiedzieć, czy jest to dopuszczalne (to brzmi trochę dziwnie, wiem).

Do tej pory używałem wszystkich powyższych wewnątrz ServletContextListener. (Nie chcę używać żadnego serwera aplikacji lub Zadania cron i chcę zachować powyższe rzeczy wewnątrz mojej aplikacji internetowej.)

Czy jest na to jakiś inteligentny sposób JSF, czy powinienem trzymać się starego wzorca?

Author: BalusC, 2011-09-21

1 answers

Wprowadzenie

Jeśli chcesz mieć możliwość odwoływania się do niego w swoich poglądach przez #{managedBeanName}lub przez inne zarządzane fasolki przez, to może to mieć sens tylko wtedy, gdy chcesz mieć możliwość odwoływania się do niego w swoich poglądach przez #{managedBeanName} lub w innych zarządzanych fasolkach przez @ManagedProperty("#{managedBeanName}"). Należy tylko upewnić się, że wdrożyć @PreDestroy aby upewnić się, że wszystkie te wątki są wyłączone, gdy aplikacja webapp ma zamiar zamknąć, jak to zrobić w contextDestroyed() metoda ServletContextListener (tak?). Zobacz też czy to można założyć nowy wątek w JSF?

Nigdy nie używaj java.util.Timer W Java EE

Jeśli chodzi o używanie java.util.Timer w fasoli zarządzanej przez JSF, powinieneś absolutnie nie używać staromodnego Timer, ale nowoczesnego ScheduledExecutorService. [12]} ma następujące główne problemy, które sprawiają, że nie nadaje się do użycia w długo działającej aplikacji internetowej Java EE (cytowany z współbieżność Java w praktyce):

  • Timer jest wrażliwy na zmiany w systemie zegar, ScheduledExecutorService nie jest.
  • Timer ma tylko jeden wątek wykonawczy, więc długo działające zadanie może opóźnić inne zadania. ScheduledExecutorService może być skonfigurowany z dowolną liczbą wątków.
  • wszelkie wyjątki uruchomieniowe rzucone w TimerTask zabijają ten jeden wątek, przez co Timer staje się martwy, tzn. zaplanowane zadania nie będą już uruchamiane. ScheduledThreadExecutor nie tylko wychwytuje wyjątki środowiska wykonawczego, ale pozwala Ci je obsługiwać, jeśli chcesz. Zadanie, które wyrzuciło wyjątek zostanie anulowane, ale inne zadania będą kontynuowane uciekaj.

Oprócz cytatów z książki, mogę myśleć o więcej wad:

  • Jeśli zapomnisz jawnie cancel() Timer, to nadal działa po niewypełnieniu. Tak więc po ponownym przesunięciu tworzy się nowy wątek, wykonując ponownie tę samą pracę. Etcetera. Stało się "fire and forget" już teraz i nie można go programowo anulować. W zasadzie musisz zamknąć i ponownie uruchomić cały serwer, aby usunąć poprzednie wątki.

  • Jeśli Timer wątek nie jest oznaczony jako wątek demona, a następnie zablokuje nieprzystosowanie aplikacji webapp i zamknięcie serwera. W zasadzie trzeba by mocno zabić serwer. Główną wadą jest to, że webapp nie będzie w stanie wykonać wdzięku czyszczenia za pomocą metod np contextDestroyed() i @PreDestroy.

EJB dostępny? Użycie @Schedule

JBoss AS, GlassFish, TomEE itp., a zatem Nie kontener JSP/Servlet typu Barebones, taki jak Tomcat), to użyj a @Singleton EJB z @Schedule metoda zamiast. W ten sposób kontener będzie się martwić o łączenie i niszczenie wątków poprzez ScheduledExecutorService. Wszystko, czego potrzebujesz, to następujący 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.
    }

} 

Jest to w razie potrzeby dostępne w zarządzanych fasolach przez @EJB:

@EJB
private BackgroundJobManager backgroundJobManager;

EJB niedostępny? Użycie ScheduledExecutorService

BEZ EJB, musiałbyś ręcznie pracować z ScheduledExecutorService. Aplikacja scoped managed bean implementation wyglądałaby jak to:

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

}

GdzieSomeDailyJob wygląda tak:

public class SomeDailyJob implements Runnable {

    @Override
    public void run() {
        // Do your job here.
    }

}

Jeśli nie musisz odwoływać się do niego w widoku lub innych zarządzanych fasolach, lepiej po prostu użyj ServletContextListener aby było oddzielone od 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();
    }

}
 65
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:27