terça-feira, 24 de maio de 2011

Tutorial sobre agendamento com o Quartz, backup do banco de dados e envio do banco/email

Olá pessoal, boa noite.

Depois de pesquisar um pouco sobre o agendamento de tarefas no Java, li sobre algumas API's e escolhi o Quartz pela flexibilidade na execução das tarefas e facilidade de implementação no projeto. Entretanto, a maioria dos exemplos e tutorias sobre esta API demonstra casos muito básicos e até inúteis, como demonstrar uma simples mensagem de aviso em um determinado intervalo de tempo, entre outros.
Por conta disto, decidi postar um exemplo bem prático que consiste no agendamento de um backup do banco de dados da aplicação Web em determinado horário e o envio de uma cópia desse backup por email usando a API do apache commons mail com o Gmail.

As linhas de código abaixo foram extraídas de exemplos de outros autores e adaptadas para a nossa situação.

Vou tentar demonstrar da forma mais simples possível:

Primeiro Passo: fazer o download dos arquivos .jar necessários.
Apache commons mail: http://linorg.usp.br/apache//commons/email/commons-email-current.jar
Quartz: http://d2zwv9pap9ylyd.cloudfront.net/quartz-1.8.3.tar.gz

Segundo Passo: vamos criar a classe com uma determinada tarefa. Em nosso caso, a tarefa será primeiramente o backup do banco de dados e em seguida a configuração do email para envio.

package teste.quartz.tarefa;

import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.MultiPartEmail;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class Tarefa implements Job {

    public void execute(JobExecutionContext jec) throws JobExecutionException {
        File diretorio = new File("/home/viper/Documentos/bkpdb"); //defino o nome do diretório
        File bkp = new File("/home/viper/Documentos/bkpdb/bkp.sql"); //defino o nome do arquivo de bkp inicial

        if (!diretorio.isDirectory()) { //caso o diretorio nao tenha sido criado, utiliza-se a expressão abaixo para criá-lo
            new File("/home/viper/Documentos/bkpdb").mkdir();
        } else {

        }

        try {
            if (!bkp.isFile()) { //verifica-se se já existe arquivo de bkp criado, caso negativo, ele cria o arquivo de bkp inicial
                System.out.println("ainda nao existe bkp criado");
                Runtime
                        .getRuntime()
                        .exec(
                                "pg_dump -U usuario bancodedados -f /home/viper/Documentos/bkpdb/bkp.sql");
            } else {
                System.out.println("ja existe bkp criado");
                while (bkp.isFile()) { //caso já exista o arquivo acima, ele criará um arquivo com o nomedoarquivo+data+hora.sql. Dessa forma, um arquivo não
                                //sobrescreve o outro;

                    GregorianCalendar calendar = new GregorianCalendar(); //utiliza-se o GregorianCalendar para pegar a data/hora correta do sistema
                    int dia = calendar.get(Calendar.DAY_OF_MONTH);
                    int mes = calendar.get(Calendar.MONTH);
                    int ano = calendar.get(Calendar.YEAR);
                    int hora = calendar.get(Calendar.HOUR_OF_DAY);
                    int minuto = calendar.get(Calendar.MINUTE);
                    int segundo = calendar.get(Calendar.SECOND);
                    bkp = new File(
                            "/home/viper/Documentos/bkpdb/bkp"
                                    + dia + mes + ano + hora + minuto + segundo
                                    + ".sql"); //nome do arquivo de bkp criado com a data/hora atual
                    System.out
                            .println("Backup realizado com sucesso. Data atual: "
                                    + dia + "/" + mes + "/" + ano + "-" + hora + ":" + minuto + ":" + segundo);
                }

                Runtime.getRuntime()
                        .exec("pg_dump -U usuario bancodedados -f" + bkp); //executa o comando com o nome do arquivo de bkp acima

                File f = new File("");
                f = bkp; //pegamos aqui sempre o nome e o caminho do ultimo arquivo criado
                System.out.println("nome do arquivo/caminho: " + f);
                EmailAttachment attachment = new EmailAttachment(); //classe utilizada para criar anexos no email
                attachment.setPath(f.getPath()); //envia o caminho do arquivo
                attachment.setDisposition(EmailAttachment.ATTACHMENT);
                attachment.setDescription("File");
                attachment.setName(f.getName());

                try {

                    MultiPartEmail email = new MultiPartEmail(); //classe utilizada para permitir anexos no email
                    email.setDebug(true);
                    email.setHostName("smtp.gmail.com"); //servidor SMTP. Aqui usamos um do Gmail
                    email.setAuthentication("login_do_email", "senha"); // login e senha da conta Gmail
                    email.setSSL(true); //Autenticação de segurança SSL setada como True
                    email.addTo("seuemail@teste.com"); //nome do email que vai receber o bkp do banco de dados. Pode ser o seu para teste
                    email.setFrom("remetentedoemail"); //endereço de email do remetente
                    email.setSubject("Bkp Base de dados"); //assunto
                    email.setMsg("Segue em anexo base de dados"); //mensagem de texto

                    email.attach(attachment); //anexa o arquivo de bkp

                    email.send(); //envia o email
                } catch (EmailException e) { //exception caso aconteça algum erro ao enviar o email
                                        System.out.println("erro no envio: " + e.printStackTrace());
                                        e.printStackTrace()
                }

            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }
}

Terceiro Passo: criar a classe que irá executar a tarefa.

package teste.quartz.tarefa;

import java.io.IOException;
import java.util.GregorianCalendar;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;

public class ExecTarefa {

    static GregorianCalendar calendar = new GregorianCalendar();

        public static void main(String[] args) {

        Trigger trigger = TriggerUtils.makeDailyTrigger("Tarefa", hora, minuto); //aqui utilizamos o "makeDailyTrigger" que executa a tarefa determinada hora/minuto

        trigger.setName("Tarefa"); //setamos o nome da tarefa

        JobDetail jobDetail = new JobDetail("Tarefa", "Tarefa group",
                Tarefa.class); //definimos o JobDetail com o nome da tarefa, o grupo e a classe que contém a tarefa a ser executada

        Scheduler scheduler; //criamos o agendamento

        try {

            scheduler = new StdSchedulerFactory().getScheduler();

            scheduler.scheduleJob(jobDetail, trigger);

            scheduler.start(); //inicializando o agendamento da tarefa
            System.out.println("Agendando bkp e enviando por email");

        } catch (SchedulerException ex) {
            ex.printStackTrace(); //caso haja algum erro no agendamento, ele retorna uma exception
        }
    }

}

Pessoal, como eu disse antes, eu fiz meio que uma salada de códigos de outros autores para viabilizar esse exemplo. Espero que seja útil a quem está passando por uma dificuldade semelhante à que eu passei.

Abs e obrigado.

Créditos:
gabrielmassote - http://www.guj.com.br/java/104645-api-commons-mail---usando-gmail;
Camilo Lopes - http://blog.camilolopes.com.br/tag/mysql/;
Outros.