Atenção: por essa edição ser muito antiga não há arquivo PDF para download.Os artigos dessa edição estão disponíveis somente através do formato HTML.
Agendamento de tarefas em Java
Usando o melhor que o Java SE e EE oferecem
Tire o máximo proveito dos recursos de agendamento disponíveis da Java SE e Java EE, e veja como criar uma aplicação Swing de reservas usando vários design patterns
Neste artigo, vamos abordar os recursos de agendamento de tarefas disponíveis em Java SE e Java EE. Apresentaremos as classes Timer e TimerTask, presentes na distribuição padrão do Java SE desde a versão 1.3, e o serviço de Timer (Timer Service) acrescentado ao EJB na versão 2.1. Para ilustrar a utilização, criaremos um sistema completo de reservas com interface Swing.
Agendamento de tarefas
O agendamento de tarefas permite programar tarefas (Jobs) para execução em momentos específicos, ou periodicamente. Este recuso é suportado nos próprios sistemas operacionais, por exemplo através do agendador de tarefas do Windows ou utilitário cron do Linux/Unix.Veja alguns cenários onde o agendamento de tarefas é útil:
· Realizar um backup periodicamente
· Criar diariamente um relatório consolidado de vendas para a gerência
· Enviar um e-mail para os usuários do sistema cuja senha tenha expirado
· Verificar a cada cinco minutos se foram copiados novos arquivos para um determinado diretório.
O Java oferece elementos para o trabalho com agendamento de tarefas, tanto em sua plataforma Standard como na Enterprise, como veremos em detalhes a seguir.
O estudo de caso
Para ilustrar a utilização do agendamento de tarefas com Java, criamos uma aplicação de exemplo que permite a criação de tarefas de verificação automática de expiração de reservas. Poderíamos utilizar este aplicativo em qualquer sistema que trabalhe com o conceito de reserva, como por exemplo hotéis, restaurantes, teatros ou empresas aéreas. A idéia é permitir que o próprio aplicativo verifique e atualize os status de reservas expiradas.
Cada reserva é representada por um objeto da classe br.com.globalcode.model.Reserva. Esta classe tem como atributos um código, uma data de expiração, um status (PENDENTE,CONFIRMADA OU CANCELADA) e um id, que corresponde à chave primária no banco de dados. Veja o código completo na Listagem1.
O acesso aos dados persistidos para as reservas é realizado através da classe br.com.globalcode.dao.ReservasDAO. Além dos métodos diretamente relacionados à manipulação de reservas, esta classe contém os métodos para obtenção e fechamento da conexão com o banco de dados. Usualmente, estes métodos são implementados em uma classe separada, de maneira a serem compartilhados entre os diversos DAOs do aplicativo, mas no exemplo, por simplificação estão na própria ReservasDAO. A figura 1 apresenta o diagrama de classe de ReservasDAO. O código completo desta classe e de todo resto da aplicação está disponível no site da Java Magazine.
Além de reserva também criamos a classe br.com.globalcode.model.Agendamento (Listagem 2), que contém apenas uma descrição e a data e o horário do agendamento. No nosso exemplo o agendamento é sempre diário; por isso esta classe não contém o intervalo para repetição. Note também que, como a classe Agendamento não precisa ser persistida, não criamos um DAO para ela.
Figura 1. Diagrama de classe de ReservasDAO
Listagem 1. Reserva.
package br.com.globalcode.model;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Reserva {
public static final SimpleDateFormat
FORMATADOR_DATA_USUARIO = new SimpleDateFormat("dd/MM/yyyy hh:mm");
private String codigo;
private Date dataExpiracao;
private int id;
private int status = PENDENTE;
public static final int PENDENTE= 1;
public static final int CANCELADA= 2;
public static final int CONFIRMADA= 3;
public Reserva() {
}
public Reserva(String codigo. Date dataExpiracao){
this.codigo = codigo;
this.dataExpiracao = dataExpiracao;
}
public Reserva(int id, String codigo, Date dataExpiracao){
this(codigo,dataExpiracao);
this.id = id;
}
public Reserva(int id, String codigo, Date dataExpiracao. int status){
this(codigo,dataExpiracao);
this.id = id;
this.status = status;
}
public String toString(){
return "Codigo = " + codigo + " data expiração = " + FORMATADOR_DATA_
USUARIO.format(dataExpiracao) + " id= " + id + "status = " + status;
}
//getters e setters omitidos
}
Listagem 2.Agendamento.
package br.com.globalcode.model;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Agendamento implements Serializable{
public static final SimpleDateFormat
FORMATADOR_DATA_USUARIO = new SimpleDateFormat("dd/MM/yyyy hh:mm");
private String info;
private Date data;
public Agendamento() {
}
public Agendamento(String info, Date data){
this.info = info;
this.data = data;
}
public String toString(){
return info + " " + FORMATADOR_DATA_USUARIO.format(data);
}
//método equals, hashcode, getters e setters omitidos
}
Agendamento de tarefas com Java SE
Desde a versão 1.3, estão disponíveis duas classes para o agendamento de tarefas no Java SE: java.util.Timer e java.util.TimerTask.
java.util.TimerTask
A classe abstrata TimerTask representa uma tarefa a ser agendada. É uma implementação de java.lang.Runnable. Portanto para criar tarefas devemos criar subclasses de TimerTask e implementar o método run(), da mesma forma que é feito quando trabalhamos com operações a serem executadas por threads. A classe permite também cancelar a tarefa (cancel()) e obter a data da sua última execução (scheduledExecutionTime()).
A classe br.com.globalcode.tasks.VerificadorReservasExpiradas ilustra um exemplo de tarefa baseada em TimerTask. Essa tarefa tem o objetivo de alterar o status de todas as reservas que expiraram até o momento. Ela utiliza a classe ReservasDAO que implementa o design pattern DAO contendo os métodos para interação com o banco de dados. Veja mais no quadro “Patterns Utilizados”. O código da classe está na Listagem 3.
Listagem 3. VerificadorReservasExpiradas.
package br.com.globalcode.tasks;
import br.com.globalcode.dao.ReservasDAO;
import java.sql.SQLException;
import java.util.*;
...