Programação na primavera com quartzo

Programação na primavera com quartzo

1. Visão geral

Neste tutorial, construiremos umScheduler in Spring with Quartz simples.

Começaremos com um objetivo simples em mente - configurar facilmente um novo trabalho agendado.

1.1. Principais componentes da API Quartz

O quartzo possui uma arquitetura modular. Consiste em vários componentes básicos que podem ser combinados conforme necessário. Neste tutorial, vamos nos concentrar naqueles que são comuns a todos os trabalhos:Job,JobDetail,TriggereScheduler.

Embora usemos Spring para gerenciar o aplicativo, cada componente individual pode ser configurado de duas maneiras: a maneiraQuartz ou a maneiraSpring (usando suas classes de conveniência).

Abordaremos tanto quanto possível por uma questão de completude, mas qualquer um pode ser adotado. Vamos começar a construir, um componente de cada vez.

Leitura adicional:

Um guia para o Agendador de tarefas Spring

Um guia rápido e prático para agendar no Spring com o Task Scheduler

Read more

Planejamento no Java EE

Uma demonstração de como agendar tarefas no Java EE usando a anotação @Schedule e o serviço de timer.

Read more

Introdução ao Drools

Aprenda a usar Drools como um sistema de gerenciamento de regras de negócios (BRMS).

Read more

2. Job eJobDetail

2.1. Job

A API fornece uma interfaceJob com apenas um método -execute. Ela deve ser implementada pela classe que contém o trabalho real a ser feito, ou seja, a tarefa. Quando o gatilho de um job é disparado, o planejador invoca o métodoexecute, passando um objetoJobExecutionContext.

OJobExecutionContext fornece à instância do job informações sobre seu ambiente de tempo de execução, incluindo um identificador para o planejador, um identificador para o acionador e o objetoJobDetail do job.

Neste exemplo rápido - o trabalho delega a tarefa a uma classe de serviço: __

@Component
public class SampleJob implements Job {

    @Autowired
    private SampleJobService jobService;

    public void execute(JobExecutionContext context) throws JobExecutionException {
        jobService.executeSampleJob();
    }
}

2.2. JobDetail

Enquanto o trabalho é o cavalo de batalha, o Quartz não armazena uma instância real da classe de trabalho. Em vez disso, podemos definir uma instância deJob usando a classeJobDetail. A classe do trabalho deve ser fornecida aoJobDetail para que ele saiba otype do trabalho a ser executado.

2.3. QuartzoJobBuilder

O QuartzJobBuilder fornece uma API de estilo construtor para construirJobDetail entidades.

@Bean
public JobDetail jobDetail() {
    return JobBuilder.newJob().ofType(SampleJob.class)
      .storeDurably()
      .withIdentity("Qrtz_Job_Detail")
      .withDescription("Invoke Sample Job service...")
      .build();
}

2.4. PrimaveraJobDetailFactoryBean

JobDetailFactoryBean do Spring fornece o uso do estilo bean para configurar instâncias deJobDetail. Ele usa o nome do bean Spring como o nome do trabalho, se não for especificado de outra forma:

@Bean
public JobDetailFactoryBean jobDetail() {
    JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
    jobDetailFactory.setJobClass(SampleJob.class);
    jobDetailFactory.setDescription("Invoke Sample Job service...");
    jobDetailFactory.setDurability(true);
    return jobDetailFactory;
}

Uma nova instância deJobDetail é criada para cada execução do trabalho. O objetoJobDetail transmite as propriedades detalhadas do trabalho. Depois que a execução é concluída, as referências à instância são descartadas.

3. Desencadear

UmTrigger é o mecanismo para agendar umJob, ou seja, uma instânciaTrigger “dispara” a execução de um trabalho. Há uma separação clara de responsabilidades entreJob (noção de tarefa) eTrigger (mecanismo de agendamento).

Além deJob, o gatilho também precisa de umtype que pode ser escolhido com base nos requisitos de programação.

Digamos que queremos agendar nossa tarefa para executaronce every hour, indefinidamente - podemos usarTriggerBuilder de Quartz ouSimpleTriggerFactoryBean de Spring para fazer isso.

3.1. QuartzoTriggerBuilder

TriggerBuilder é uma API de estilo construtor para construir a entidadeTrigger:

@Bean
public Trigger trigger(JobDetail job) {
    return TriggerBuilder.newTrigger().forJob(job)
      .withIdentity("Qrtz_Trigger")
      .withDescription("Sample trigger")
      .withSchedule(simpleSchedule().repeatForever().withIntervalInHours(1))
      .build();
}

3.2. PrimaveraSimpleTriggerFactoryBean

SimpleTriggerFactoryBean fornece o uso do estilo bean para configurarSimpleTrigger. Ele usa o nome do bean Spring como o nome do acionador e o padrão é repetição indefinida, se não especificado de outra forma:

@Bean
public SimpleTriggerFactoryBean trigger(JobDetail job) {
    SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
    trigger.setJobDetail(job);
    trigger.setRepeatInterval(3600000);
    trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
    return trigger;
}

4. Configurando oJobStore

JobStore fornece o mecanismo de armazenamento paraJobeTrigger e é responsável por manter todos os dados relevantes para o planejador de job. A API suporta armazenamentosin-memory epersistent.

4.1. Na memóriaJobStore

Para fins de exemplo, usaremos oRAMJobStore na memória, que oferece desempenho extremamente rápido e configuração simples viaquartz.properties:

org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore

A desvantagem óbvia deRAMJobStore é que ele évolatile por natureza. Todas as informações de agendamento são perdidas entre os desligamentos. Se as definições e planejamentos de trabalho devem ser mantidos entre desligamentos, o persistenteJDBCJobStore deve ser usado em seu lugar. Para habilitar umJobStore na memória no Spring,, definimos esta propriedade em nossoapplication.properties:

spring.quartz.job-store-type=memory

4.2. JDBCJobStore

Existem dois tipos deJDBCJobStore:JobStoreTXeJobStoreCMT. Ambos fazem o mesmo trabalho de armazenamento de informações de agendamento em um banco de dados.

A diferença entre os dois é como eles gerenciam as transações que confirmam os dados. O tipoJobStoreCMT requer uma transação do aplicativo para armazenar dados, enquanto o tipoJobStoreTX inicia e gerencia suas próprias transações.

Existem várias propriedades para definir para umJDBCJobStore. No mínimo, devemos especificar o tipo deJDBCJobStore, a fonte de dados e a classe do driver do banco de dados. Existem classes de driver para a maioria dos bancos de dados, masStdJDBCDelegate cobre a maioria dos casos:

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource=quartzDataSource

A configuração de um JDBCJobStore no Spring requer algumas etapas. Em primeiro lugar, definimos o tipo de loja em nossoapplication.properties:

spring.quartz.job-store-type=jdbc

Em seguida, precisamos ativar a configuração automática e fornecer ao Spring a fonte de dados necessária ao agendador do Quartz. A anotação@QuartzDataSource faz o trabalho difícil de configurar e inicializar o banco de dados Quartz para nós:

@Configuration
@EnableAutoConfiguration
public class SpringQrtzScheduler {

    @Bean
    @QuartzDataSource
    public DataSource quartzDataSource() {
        return DataSourceBuilder.create().build();
    }
}

5. Scheduler

A interfaceScheduler é a API principal para fazer a interface com o planejador de trabalho.

UmScheduler pode ser instanciado com umSchedulerFactory. Depois de criado,Jobs eTriggers podem ser registrados nele. Inicialmente, oScheduler está no modo “stand-by” e seu métodostart deve ser invocado para iniciar os threads que acionam a execução de jobs.

5.1. QuartzoStdSchedulerFactory

Simplesmente invocando o métodogetScheduler emStdSchedulerFactory, podemos instanciar oScheduler, inicializá-lo (comJobStoreeThreadPool configurados) e retornar um lidar com sua API:

@Bean
public Scheduler scheduler(Trigger trigger, JobDetail job, SchedulerFactoryBean factory)
  throws SchedulerException {
    Scheduler scheduler = factory.getScheduler();
    scheduler.scheduleJob(job, trigger);
    scheduler.start();
    return scheduler;
}

5.2. PrimaveraSchedulerFactoryBean

SchedulerFactoryBean do Spring fornece o uso do estilo bean para configurar umScheduler, gerencia seu ciclo de vida dentro do contexto do aplicativo e expõe oScheduler como um bean para injeção de dependência:

@Bean
public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job, DataSource quartzDataSource) {
    SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
    schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));

    schedulerFactory.setJobFactory(springBeanJobFactory());
    schedulerFactory.setJobDetails(job);
    schedulerFactory.setTriggers(trigger);
    schedulerFactory.setDataSource(quartzDataSource);
    return schedulerFactory;
}

5.3. ConfigurandoSpringBeanJobFactory

OSpringBeanJobFactory fornece suporte para injetar o contexto do planejador, mapa de dados do trabalho e entradas de dados do gatilho como propriedades no bean de trabalho durante a criação de uma instância.

No entanto, ele carece de suporte para a injeção de referências de bean deapplication context. Graças ao autor dethis blog post, podemos adicionar suporteauto-wiring aSpringBeanJobFactory assim:

@Bean
public SpringBeanJobFactory springBeanJobFactory() {
    AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    return jobFactory;
}

6. Conclusão

Isso é tudo. Acabamos de construir nosso primeiro planejador básico usando a API Quartz, bem como as classes de conveniência do Spring.

A principal dica deste tutorial é que conseguimos configurar um trabalho com apenas algumas linhas de código e sem usar nenhuma configuração baseada em XML.

Osource code completo para o exemplo está disponível emthis github project. É um projeto Maven que pode ser importado e executado como está. A configuração padrão usa as classes de conveniência do Spring, que podem ser facilmente trocadas para a API Quartz com um parâmetro de tempo de execução (consulte o README.md no repositório).