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?
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 coTimer
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 npcontextDestroyed()
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();
}
}
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