Agendando tarefas com o TimerService do EJB 3.1

Alura
steppat
steppat

Compartilhe

versão 3 dos Enterprise Java Beans trouxe grandes mudanças e muitas simplificações para o desenvolvedor. O forte uso de anotações e convenções, que deixaram os XMLs complexos opcionais, entrou no JPA como forma padrão de persistência para substituir os burocráticos entity beans e a injeção de dependências melhora o design para não depender de lookups acoplados.

 O EJB 3.1 foi um passo na mesma direção. Nessa versão as interfaces locais ficaram opcionais, os EJBs podem ser utilizados dentro de um WAR, o que simplifica o empacotamento (EAR não é mais preciso), e várias outras novidades.

Uma das melhorias do EJB 3.1 está relacionada com o agendamento de tarefas dentro do servidor de aplicação, o que é o foco desse post. Mas, para ser exato, o agendamento já era possível nas versões anteriores do EJB (entrou na versão 2.1 da especificação), mas foi muito aperfeiçoado no EJB 3.1.

Banner da Imersão Agentes de IA da Alura com Google Gemini. Participe de 3 aulas gratuitas online, com certificado, para você aprender a criar agentes de IA que pensam por você! Inscreva-se agora e garanta a sua vaga e participe de uma comunidade exclusiva no discord!

Como funcionava com EJB 3.0/2.1

Primeiro era preciso definir o método que era chamado quando o @Timeout de um agendamento ocorria. Para isso podemos usar um Session Bean Stateless:

 @Stateless @Remote(Agendador.class) //pode ser @Local também public class AgendadorBean implements Agendador {
@Timeout // no EJB 2.1 implementava a interface javax.ejb.TimedObject public void timeout(Timer timer) { System.out.println("Timeout: " + timer.getInfo()); } 

Para agendar a execução desse método, usamos o TimerService. Com ele podemos definir o agendamento e executar um método anotado com @Timeout apenas uma vez, ou em intervalos (single-action ou interval-action). O TimerService pode ser injetado, com EJB3, da seguinte maneira:

 @Stateless @Remote(Agendador.class) public class AgendadorBean implements Agendador {
@Resource //no EJB 2.1 era preciso usar ejbContext.getTimerService() private TimerService timerService;
public void agenda() { //definir o agendamento - daqui 10s, cada 20s this.timerService.createTimer(10\*1000L, 20\*1000L, "alguma info"); } } 

O método createTimer(..) é sobrecarregado e possui variações, mas não tem como fazer mais do que definir single-action-timer ou interval-action.

O que melhorou com EJB 3.1

No EJB 3.1 o TimerService ganhou métodos para deixar o agendamento mais preciso, baseado em expressões de calendar. Isso é bem parecido com o que o framework Quartz permite no Java, e as expressões cron:

 public void agenda() { // cada segunda e quarta as 8:30 ScheduleExpression expression = new ScheduleExpression(); expression.dayOfWeek("Mon,Wed"); expression.hour("8"); expression.minute("30"); this.timerService.createCalendarTimer(expression); System.out.println("Agendado: " + expression); } 

Com essas expressões podemos definir intervalos bem precisos e a mesma definição também pode ser feita de forma declarativa, através da anotação @Schedule. Com ela nem é necessário usar @Timeout. O método anotado com @Schedule é invocado quando o timeout ocorre:

 //dentro do session bean @Schedule(dayOfWeek="Mon,Wed", hour="8", minute="30") void agendado() { System.out.println("agendado pela anotacao @Schedule"); } 

O servidor de aplicação chama então o agendado() periodicamente. Importante saber que qualquer timer que definir um intervalo (interval-action-timer) é persistido e será recuperado quando o servidor reiniciar. Mas podemos deixar o agendamento não persistente também:

 //dentro do session bean @Schedule(dayOfWeek="Mon,Wed", hour="8", minute="30", persistent=false) void agendado() { System.out.println("agendado pela anotacao @Schedule"); } 

ou ser for agendado programaticamente:

 ScheduleExpression expression = new ScheduleExpression(); //.. TimerConfig config = new TimerConfig(); config.setPersistent(false); this.timerService.createCalendarTimer(expression, config); 

Poderíamos ainda melhorar o exemplo usando @Singleton e @Startup, outras novidades do EJB 3.1. Isso pode ser útil se for utilizado o agendamento através do @Schedule não persistente. E claro, isso é apenas mais uma das facilidades que o container EJB e o servidor de aplicação podem oferecer, todos vistos no treinamento Java EE avançado e Web Services.

Veja outros artigos sobre Inovação & Gestão