Planejamento no Java EE
1. Visão geral
Em umarticle anterior, demonstramos como agendar tarefas no Spring usando a anotação* @Scheduled *__. Neste artigo, demonstraremos como conseguir o mesmo usandothe timer service in a Java Enterprise Edition application para cada caso apresentado no artigo anterior.
2. Ativar suporte para agendamento
Em um aplicativo Java EE, não há necessidade de ativar o suporte para tarefas programadas. O serviço de timer é um serviço gerenciado por contêiner que permite aos aplicativos chamar métodos agendados para eventos baseados em tempo. Como exemplo, um aplicativo pode precisar executar alguns relatórios diários em uma determinada hora para gerar estatísticas.
Existem dois tipos de temporizadores:
-
Programmatic timers: o serviço de cronômetro pode ser injetado em qualquer bean (exceto um bean de sessão com estado) e a lógica de negócios deve ser colocada em um método anotado com@Timeout. O cronômetro pode ser inicializado por um método anotado em@PostConstruct dos beans ou também pode ser inicializado apenas chamando um método.
-
Automatic timers: a lógica de negócios é colocada em qualquer método anotado com@Schedule ou@Schedules. Esses cronômetros são inicializados assim que o aplicativo é iniciado.
Então, vamos começar com nosso primeiro exemplo.
3. Agendar tarefa com um atraso fixo
No Spring, isso é feito simplesmente usando a anotação@Scheduled(fixedDelay = 1000). Nesse caso, a duração entre o final da última execução e o início da próxima execução é fixa. A tarefa sempre espera até que a anterior seja concluída.
Fazer exatamente a mesma coisa no Java EE é um pouco mais difícil de conseguir, porque não existe um mecanismo interno semelhante, no entanto, um cenário semelhante pode ser implementado com um pouco de codificação extra. Vamos dar uma olhada em como isso é feito:
@Singleton
public class FixedTimerBean {
@EJB
private WorkerBean workerBean;
@Lock(LockType.READ)
@Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void atSchedule() throws InterruptedException {
workerBean.doTimerWork();
}
}
@Singleton
public class WorkerBean {
private AtomicBoolean busy = new AtomicBoolean(false);
@Lock(LockType.READ)
public void doTimerWork() throws InterruptedException {
if (!busy.compareAndSet(false, true)) {
return;
}
try {
Thread.sleep(20000L);
} finally {
busy.set(false);
}
}
}
Como você pode ver, o cronômetro está programado para ser disparado a cada cinco segundos. No entanto, o método disparado em nosso caso simulou um tempo de resposta de 20 segundos pela chamadasleep() noThread atual.
Como consequência, o contêiner continuará a chamardoTimerWork() a cada cinco segundos, mas a condição colocada no início do método,busy.compareAndSet(false, true), retornará imediatamente se a chamada anterior não tiver terminado. Nesse sentido, garantimos que a próxima tarefa seja executada somente após a conclusão da anterior.
4. Agendar tarefa a uma taxa fixa
Uma maneira de fazer isso é usar otimer service que é injetado usando@Resourcee configurado no método anotado@PostConstruct. O método anotado com@Timeout será chamado quando o cronômetro expirar.
Conforme mencionado no artigo anterior, o início da execução da tarefa não espera pelo término da execução anterior. Esta opção deve ser usada quando cada execução da tarefa é independente. O seguinte trecho de código cria um timer que é acionado a cada segundo:
@Startup
@Singleton
public class ProgrammaticAtFixedRateTimerBean {
@Inject
Event event;
@Resource
TimerService timerService;
@PostConstruct
public void initialize() {
timerService.createTimer(0,1000, "Every second timer with no delay");
}
@Timeout
public void programmaticTimout(Timer timer) {
event.fire(new TimerEvent(timer.getInfo().toString()));
}
}
Outra forma é usar a anotação@Scheduled. No seguinte snippet de código, disparamos um timer a cada cinco segundos:
@Startup
@Singleton
public class ScheduleTimerBean {
@Inject
Event event;
@Schedule(hour = "*", minute = "*", second = "*/5", info = "Every 5 seconds timer")
public void automaticallyScheduled(Timer timer) {
fireEvent(timer);
}
private void fireEvent(Timer timer) {
event.fire(new TimerEvent(timer.getInfo().toString()));
}
}
5. Agendar tarefa com atraso inicial
Se o seu cenário de caso de uso exigir que o cronômetro inicie com um atraso, também podemos fazer isso. Nesse caso, o Java EE permite o uso detimer service. Vamos dar uma olhada em um exemplo em que o cronômetro tem um atraso inicial de 10 segundos e dispara a cada cinco segundos:
@Startup
@Singleton
public class ProgrammaticWithInitialFixedDelayTimerBean {
@Inject
Event event;
@Resource
TimerService timerService;
@PostConstruct
public void initialize() {
timerService.createTimer(10000, 5000, "Delay 10 seconds then every 5 seconds timer");
}
@Timeout
public void programmaticTimout(Timer timer) {
event.fire(new TimerEvent(timer.getInfo().toString()));
}
}
O métodocreateTimer usado em nossa amostra está usando a seguinte assinatura createTimer(long initialDuration, long intervalDuration, java.io.Serializable info) ondeinitialDuration é o número de milissegundos que deve decorrer antes da primeira notificação de expiração do temporizador eintervalDuration é o número de milissegundos que deve decorrer entre as notificações de expiração do cronômetro.
Neste exemplo, estamos usando uminitialDuration de 10 segundos e umintervalDuration de cinco segundos. A tarefa será executada pela primeira vez após o valor deinitialDuration e continuará a ser executada de acordo com ointervalDuration.
6. Agendar tarefas usando expressões Cron
Todos os planejadores que vimos, programáticos e automáticos, permitem o uso de expressões cron. Vamos ver um exemplo:
@Schedules ({
@Schedule(dayOfMonth="Last"),
@Schedule(dayOfWeek="Fri", hour="23")
})
public void doPeriodicCleanup() { ... }
Neste exemplo, o métododoPeriodicCleanup() será chamado todas as sextas-feiras às 23:00 e no último dia do mês.
7. Conclusão
Neste artigo, vimos várias maneiras deschedule tasks in the Java EE environment usando como ponto de partida um artigo anterior onde as amostras foram feitas usando Spring.
As amostras de código podem ser encontradas emGitHub repository.