Back-End

12 ago, 2014

Rastreamento de Exceções: agendamento com Spring – Parte 05

Publicidade

Parece que estou finalmente chegando ao fim desta série de artigos sobre Rastreamento de Erros usando o Spring e, para quem ainda não leu nenhum texto da série, estou escrevendo um aplicativo Spring simples, mas de qualidade quase corporativa que verifica exceções em arquivos de log e depois gera um relatório. Desde o primeiro artigo da série, estes eram meus requisitos iniciais:

  1. Busque um determinado diretório e (possivelmente) seus subdiretórios à procura de arquivos de um determinado tipo.
  2. Se um arquivo for encontrado, verifique sua data: precisa ser feita uma busca de erros?
  3. Se o arquivo é recente o suficiente para ser verificado, valide-o para buscar exceções.
  4. Caso contenham exceções, são elas que estamos procurando ou já foram excluídas?
  5. Se o arquivo contém o tipo de exceção que estamos procurando, adicione os detalhes a um relatório.
  6. Quando todos os arquivos tiverem sido verificados, formate o relatório que está pronto para ser publicado.
  7. Publique o relatório usando e-mail ou outra técnica.
  8. Tudo será executado em uma determinada hora, todos os dias.

Este artigo dá detalhes sobre como cumprir o requisito número 8: “Tudo será executado em uma determinada hora, todos os dias”, e isso significa implementar algum tipo de agendamento.

O Java está aí por um período que já nos dá a impressão de ser longo, o que significa que há várias maneiras de agendar uma tarefa. Estas vão desde:

  • Usar uma thread simples com um longo sleep(…).
  • Usar objetos Timer e TimerTask.
  • Usar ScheduledExecutorService.
  • Usar as classes TaskExecutor e TaskScheduler do Spring.
  • Usar as anotações @EnableScheduling e @Scheduled do Spring (Spring 3.1 em diante).
  • Usar um agendador mais profissional.

Os tipos mais profissionais de agendadores variam do Quartz (gratuito) ao Obsidian (aparentemente muito mais avançado, mas pago). O Spring, como esperado, inclui suporte para Quartz Scheduler. Na verdade, há duas maneiras de integrar o Quartz Scheduler a seu app Spring, e estas são:

1. Usar um JobDetailBean
2. Usar um MethodInvokingJobDetailFactoryBean

Para este aplicativo, estou usando a integração com o Quartz do Spring junto com um MethodInvokingJobDetailFactoryBean. O motivo é que o uso do Quartz me permite configurar minha agenda usando uma expressão cron, e o MethodInvokingJobDetailFactoryBean pode ser configurado rápida e simplesmente usando algumas linhas de XML.

A técnica de expressão cron usada por Spring e Quartz foi descaradamente tirada do agendador cron do Unix. Para obter mais informações sobre como o Quartz lida com expressões de cron, dê uma olhada na página cron Quartz. Se precisar de ajuda para criar suas próprias expressões cron, você vai ver que aquele Cron Maker é um utilitário que ajuda muito.

A primeira coisa a fazer ao configurar Spring e Quartz é incluir as seguintes dependências para seu arquivo de projeto POM:

          <!-- QuartzJobBean is in spring-context-support.jar -->
          <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-context-support</artifactId>
               <version>${org.springframework-version}</version>
               <exclusions>
                    <!-- Exclude Commons Logging in favour of SLF4j -->
                    <exclusion>
                         <groupId>commons-logging</groupId>
                         <artifactId>commons-logging</artifactId>
                    </exclusion>
               </exclusions>
          </dependency>
          <!-- Spring + Quartz need transactions -->
          <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-tx</artifactId>
               <version>${org.springframework-version}</version>
          </dependency>
          <!-- Quartz framework -->
          <dependency>
               <groupId>org.quartz-scheduler</groupId>
               <artifactId>quartz</artifactId>
               <version>1.8.6</version>
               <!-- You can't use Quartz two with Spring 3 -->
          </dependency>

Isso acontece de forma razoavelmente direta com uma minúscula pegadinha no final. Em primeiro lugar, o Quartz do Spring está localizado em spring-context-support-3.2.7.RELEASE.jar (substitua o número da sua versão do Spring se for o caso). Em segundo lugar, você também precisa incluir a biblioteca de transação do Spring – spring-td-3.2.7.RELEASE.jar. Por último, inclua uma versão do agendador Quartz; tenha cuidado, porém, pois o Spring 3.x e o Quartz 2.x não trabalham juntos “fora da caixa” (mas se você procurar por aí pode encontrar correções ad-hoc). Usei a versão 1.8.6 do Quartz, que faz exatamente o que eu preciso que ele faça.

O próximo passo é escolher a configuração XML, e isso envolve três etapas:

  1. Crie uma instância de MethodInvokingJobDetailFactoryBean. Ela tem duas propriedades: o nome do bean que você deseja chamar em um intervalo agendado e o nome do método nesse bean.
  2. Conecte o MethodInvokingJobDetailFactoryBean a uma expressão cron usando um CronTriggerFactoryBean.
  3. Por último, agende esse troço todo usando um SchedulerFactoryBean.

Depois de configurar estes três beans, você recebe um XML que é mais ou menos assim:

    <bean id="FileLocatorJob"
          class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

          <property name="targetObject" ref="errorTrackService" />
          <property name="targetMethod" value="trackErrors" />

     </bean>

     <bean id="FileLocatorTrigger"
          class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
          <property name="jobDetail" ref="FileLocatorJob" />
          <!-- run every morning at 2 AM -->
          <property name="cronExpression" value="${cron.expression}" />
     </bean>

     <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
          <property name="triggers">
               <list>
                    <ref bean="FileLocatorTrigger" />
                    <!-- Add other triggers for other jobs (if any) here <ref bean="" /> -->
               </list>
          </property>
     </bean>

Note que reservei um lugar para minha expressão cron. A expressão cron real pode ser encontrada no arquivo app.properties:

# run every morning at 2 AM 
cron.expression=0 0 2 * * ?

# Use this to test the app (every minute) 
#cron.expression=0 0/1 * * * ?

Aqui eu tenho duas expressões: uma que agenda o trabalho para ser executado todos os dias às 2h da manhã e outra, sobre a qual comentei, que executa o trabalho a cada minuto. Essa instância do aplicativo não tem força exatamente industrial. Se houvesse um app “adequado”, eu provavelmente usaria um conjunto diferente de propriedades em cada ambiente (DEV, UAT e produção etc.).

Há apenas algumas etapas a serem cumpridas antes que este app esteja pronto para ser lançado, e a primeira é criar um arquivo JAR executável. Vou falar mais disso na próxima.

O código para este artigo está disponível no GitHub. Se quiser ver outros artigos desta série, dê uma olhada aqui:

***

Artigo traduzido pela Redação iMasters com autorização do autor. Publicado originalmente em  http://www.captaindebug.com/2014/04/tracking-exceptions-part-5-scheduling.html